This commit is contained in:
yaohunya 2024-06-22 19:31:57 +08:00
commit 834f535304
107 changed files with 7761 additions and 0 deletions

8
.idea/.gitignore vendored Normal file
View File

@ -0,0 +1,8 @@
# 默认忽略的文件
/shelf/
/workspace.xml
# 基于编辑器的 HTTP 客户端请求
/httpRequests/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml

13
.idea/McLiveAPI.iml Normal file
View File

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<module version="4">
<component name="FacetManager">
<facet type="minecraft" name="Minecraft">
<configuration>
<autoDetectTypes>
<platformType>SPIGOT</platformType>
<platformType>BUNGEECORD</platformType>
</autoDetectTypes>
</configuration>
</facet>
</component>
</module>

View File

@ -0,0 +1,12 @@
<component name="ArtifactManager">
<artifact type="jar" name="McLiveAPI">
<output-path>$PROJECT_DIR$/out/artifacts/McLiveAPI</output-path>
<root id="archive" name="McLiveAPI.jar">
<element id="module-output" name="McLiveAPI" />
<element id="extracted-dir" path="$PROJECT_DIR$/lib/fastjson-2.0.43.jar" path-in-jar="/" />
<element id="extracted-dir" path="$PROJECT_DIR$/lib/hutool-all-5.8.23.jar" path-in-jar="/" />
<element id="extracted-dir" path="$PROJECT_DIR$/lib/Java-WebSocket-1.5.5.jar" path-in-jar="/" />
<element id="extracted-dir" path="$PROJECT_DIR$/lib/protobuf-java-3.25.1.jar" path-in-jar="/" />
</root>
</artifact>
</component>

13
.idea/compiler.xml Normal file
View File

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CompilerConfiguration">
<annotationProcessing>
<profile name="Maven default annotation processors profile" enabled="true">
<sourceOutputDir name="target/generated-sources/annotations" />
<sourceTestOutputDir name="target/generated-test-sources/test-annotations" />
<outputRelativeToContentRoot value="true" />
<module name="McLiveAPI" />
</profile>
</annotationProcessing>
</component>
</project>

20
.idea/jarRepositories.xml Normal file
View File

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="RemoteRepositoriesConfiguration">
<remote-repository>
<option name="id" value="central" />
<option name="name" value="Central Repository" />
<option name="url" value="https://repo.maven.apache.org/maven2" />
</remote-repository>
<remote-repository>
<option name="id" value="central" />
<option name="name" value="Maven Central repository" />
<option name="url" value="https://repo1.maven.org/maven2" />
</remote-repository>
<remote-repository>
<option name="id" value="jboss.community" />
<option name="name" value="JBoss Community repository" />
<option name="url" value="https://repository.jboss.org/nexus/content/repositories/public/" />
</remote-repository>
</component>
</project>

20
.idea/misc.xml Normal file
View File

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="EntryPointsManager">
<list size="2">
<item index="0" class="java.lang.String" itemvalue="org.bukkit.event.EventHandler" />
<item index="1" class="java.lang.String" itemvalue="net.md_5.bungee.event.EventHandler" />
</list>
</component>
<component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="MavenProjectsManager">
<option name="originalFiles">
<list>
<option value="$PROJECT_DIR$/pom.xml" />
</list>
</option>
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="17" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/out" />
</component>
</project>

124
.idea/uiDesigner.xml Normal file
View File

@ -0,0 +1,124 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Palette2">
<group name="Swing">
<item class="com.intellij.uiDesigner.HSpacer" tooltip-text="Horizontal Spacer" icon="/com/intellij/uiDesigner/icons/hspacer.svg" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="1" hsize-policy="6" anchor="0" fill="1" />
</item>
<item class="com.intellij.uiDesigner.VSpacer" tooltip-text="Vertical Spacer" icon="/com/intellij/uiDesigner/icons/vspacer.svg" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="1" anchor="0" fill="2" />
</item>
<item class="javax.swing.JPanel" icon="/com/intellij/uiDesigner/icons/panel.svg" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3" />
</item>
<item class="javax.swing.JScrollPane" icon="/com/intellij/uiDesigner/icons/scrollPane.svg" removable="false" auto-create-binding="false" can-attach-label="true">
<default-constraints vsize-policy="7" hsize-policy="7" anchor="0" fill="3" />
</item>
<item class="javax.swing.JButton" icon="/com/intellij/uiDesigner/icons/button.svg" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="3" anchor="0" fill="1" />
<initial-values>
<property name="text" value="Button" />
</initial-values>
</item>
<item class="javax.swing.JRadioButton" icon="/com/intellij/uiDesigner/icons/radioButton.svg" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" />
<initial-values>
<property name="text" value="RadioButton" />
</initial-values>
</item>
<item class="javax.swing.JCheckBox" icon="/com/intellij/uiDesigner/icons/checkBox.svg" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" />
<initial-values>
<property name="text" value="CheckBox" />
</initial-values>
</item>
<item class="javax.swing.JLabel" icon="/com/intellij/uiDesigner/icons/label.svg" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="0" anchor="8" fill="0" />
<initial-values>
<property name="text" value="Label" />
</initial-values>
</item>
<item class="javax.swing.JTextField" icon="/com/intellij/uiDesigner/icons/textField.svg" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
<preferred-size width="150" height="-1" />
</default-constraints>
</item>
<item class="javax.swing.JPasswordField" icon="/com/intellij/uiDesigner/icons/passwordField.svg" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
<preferred-size width="150" height="-1" />
</default-constraints>
</item>
<item class="javax.swing.JFormattedTextField" icon="/com/intellij/uiDesigner/icons/formattedTextField.svg" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
<preferred-size width="150" height="-1" />
</default-constraints>
</item>
<item class="javax.swing.JTextArea" icon="/com/intellij/uiDesigner/icons/textArea.svg" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JTextPane" icon="/com/intellij/uiDesigner/icons/textPane.svg" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JEditorPane" icon="/com/intellij/uiDesigner/icons/editorPane.svg" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JComboBox" icon="/com/intellij/uiDesigner/icons/comboBox.svg" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="0" hsize-policy="2" anchor="8" fill="1" />
</item>
<item class="javax.swing.JTable" icon="/com/intellij/uiDesigner/icons/table.svg" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JList" icon="/com/intellij/uiDesigner/icons/list.svg" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="2" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JTree" icon="/com/intellij/uiDesigner/icons/tree.svg" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JTabbedPane" icon="/com/intellij/uiDesigner/icons/tabbedPane.svg" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3">
<preferred-size width="200" height="200" />
</default-constraints>
</item>
<item class="javax.swing.JSplitPane" icon="/com/intellij/uiDesigner/icons/splitPane.svg" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3">
<preferred-size width="200" height="200" />
</default-constraints>
</item>
<item class="javax.swing.JSpinner" icon="/com/intellij/uiDesigner/icons/spinner.svg" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" />
</item>
<item class="javax.swing.JSlider" icon="/com/intellij/uiDesigner/icons/slider.svg" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" />
</item>
<item class="javax.swing.JSeparator" icon="/com/intellij/uiDesigner/icons/separator.svg" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3" />
</item>
<item class="javax.swing.JProgressBar" icon="/com/intellij/uiDesigner/icons/progressbar.svg" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1" />
</item>
<item class="javax.swing.JToolBar" icon="/com/intellij/uiDesigner/icons/toolbar.svg" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1">
<preferred-size width="-1" height="20" />
</default-constraints>
</item>
<item class="javax.swing.JToolBar$Separator" icon="/com/intellij/uiDesigner/icons/toolbarSeparator.svg" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="0" anchor="0" fill="1" />
</item>
<item class="javax.swing.JScrollBar" icon="/com/intellij/uiDesigner/icons/scrollbar.svg" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="0" anchor="0" fill="2" />
</item>
</group>
</component>
</project>

12
McLiveAPI.iml Normal file
View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<module version="4">
<component name="FacetManager">
<facet type="minecraft" name="Minecraft">
<configuration>
<autoDetectTypes>
<platformType>SPIGOT</platformType>
</autoDetectTypes>
</configuration>
</facet>
</component>
</module>

BIN
lib/BungeeCord.jar Normal file

Binary file not shown.

BIN
lib/CDTimeAPI.jar Normal file

Binary file not shown.

Binary file not shown.

BIN
lib/NBTAPI.jar Normal file

Binary file not shown.

BIN
lib/fastjson-2.0.43.jar Normal file

Binary file not shown.

BIN
lib/hutool-all-5.8.23.jar Normal file

Binary file not shown.

Binary file not shown.

BIN
lib/spigot-api-1.18.2.jar Normal file

Binary file not shown.

Binary file not shown.

52
pom.xml Normal file
View File

@ -0,0 +1,52 @@
<?xml c="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>McLiveAPI</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>16</source>
<target>16</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.2.4</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<createDependencyReducedPom>false</createDependencyReducedPom>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
</resources>
</build>
</project>

View File

@ -0,0 +1,9 @@
package com.io.yutian.livemutually.liveroom;
public interface Chat {
String content();
User user();
}

View File

@ -0,0 +1,7 @@
package com.io.yutian.livemutually.liveroom;
public interface Follow {
User user();
}

View File

@ -0,0 +1,11 @@
package com.io.yutian.livemutually.liveroom;
public interface Gift {
User user();
String name();
long count();
}

View File

@ -0,0 +1,52 @@
package com.io.yutian.livemutually.liveroom;
import com.io.yutian.livemutually.wss.KSWebSocketClient;
import com.io.yutian.mclive.Main;
import org.bukkit.entity.Player;
import java.net.URI;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class KSLiveRoomClient {
private static final String URL = "ws://localhost:8765";
private String roomId;
private Map<String, String> headMap = new HashMap<>();
private KSWebSocketClient socketClient;
private ExecutorService executor = Executors.newCachedThreadPool();
public KSLiveRoomClient(String roomId) {
this.roomId = roomId;
headMap.put("User-Agent", "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/14.0.835.163 Safari/535.1");
headMap.put("Content-Type", "text/plain;charset=UTF-8");
headMap.put("accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9");
headMap.put("cookie", "__ac_nonce=0638733a400869171be51");
}
public KSWebSocketClient getSocketClient() {
return socketClient;
}
public void openRoom(Player player) throws Exception {
URI uri = new URI(URL);
KSWebSocketClient ksWebSocketClient = new KSWebSocketClient(player, uri, headMap);
socketClient = ksWebSocketClient;
Main.LiveRoomClientList.add(ksWebSocketClient);
executor.submit(() -> socketClient.run());
}
public void close() {
if (socketClient == null || socketClient.isClosed() || socketClient.isClosing()) {
return;
}
socketClient.setForcedClose(true);
socketClient.close();
}
public String getRoomId() {
return roomId;
}
}

View File

@ -0,0 +1,9 @@
package com.io.yutian.livemutually.liveroom;
public interface Like {
User user();
long count();
}

View File

@ -0,0 +1,74 @@
package com.io.yutian.livemutually.liveroom;
import java.util.Objects;
import java.util.function.Consumer;
public class LiveRoomWatcher {
protected Consumer<Chat> chatHandler;
protected Consumer<User> userHandler;
protected Consumer<Like> likeHandler;
protected Consumer<Follow> followHandler;
protected Consumer<Gift> giftHandler;
public final LiveRoomWatcher onChat(Consumer<Chat> handler) {
Objects.requireNonNull(handler);
this.chatHandler = handler;
return this;
}
public final LiveRoomWatcher onUser(Consumer<User> handler) {
Objects.requireNonNull(handler);
this.userHandler = handler;
return this;
}
public final LiveRoomWatcher onLike(Consumer<Like> handler) {
Objects.requireNonNull(handler);
this.likeHandler = handler;
return this;
}
public final LiveRoomWatcher onFollow(Consumer<Follow> handler) {
Objects.requireNonNull(handler);
this.followHandler = handler;
return this;
}
public final LiveRoomWatcher onGift(Consumer<Gift> handler) {
Objects.requireNonNull(handler);
this.giftHandler = handler;
return this;
}
public void callChat(Chat chat) {
if (chatHandler != null) {
chatHandler.accept(chat);
}
}
public void callUser(User user) {
if (userHandler != null) {
userHandler.accept(user);
}
}
public void callLike(Like like) {
if (likeHandler != null) {
likeHandler.accept(like);
}
}
public void callFollow(Follow follow) {
if (followHandler != null) {
followHandler.accept(follow);
}
}
public void callGift(Gift gift) {
if (giftHandler != null) {
giftHandler.accept(gift);
}
}
}

View File

@ -0,0 +1,9 @@
package com.io.yutian.livemutually.liveroom;
public interface User {
String nickName();
String avatar();
}

View File

@ -0,0 +1,157 @@
package com.io.yutian.livemutually.wss;
import com.io.yutian.livemutually.liveroom.KSLiveRoomClient;
import com.io.yutian.livemutually.liveroom.LiveRoomWatcher;
import com.io.yutian.livemutually.liveroom.User;
import com.io.yutian.mclive.Main;
import json.JSONObject;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.scheduler.BukkitRunnable;
import org.java_websocket.client.WebSocketClient;
import org.java_websocket.drafts.Draft_6455;
import org.java_websocket.handshake.ServerHandshake;
import java.net.URI;
import java.util.HashMap;
import java.util.Map;
public class KSWebSocketClient extends WebSocketClient {
private Player player;
private LiveRoomWatcher liveRoomWatcher;
public Player getPlayer() {
return player;
}
public KSWebSocketClient(Player player, URI uri, Map<String, String> httpHeaders) {
super(uri, new Draft_6455(), httpHeaders);
this.player = player;
this.liveRoomWatcher = new KSAPILiveRoomWatcher(player);
Bukkit.getConsoleSender().sendMessage("#WebSocketClient 已创建.");
}
@Override
public void onOpen(ServerHandshake serverHandshake) {
this.send();
}
public HashMap<String,String> hashMap = new HashMap<>();
@Override
public void onMessage(String s) {
//if(s.isEmpty()){return;}
Bukkit.getConsoleSender().sendMessage("TEST = "+s);
Bukkit.getConsoleSender().sendMessage("TEST = "+new JSONObject(s));
JSONObject jsonObject = new JSONObject(s);
/*String type = jsonObject.getString("type");
if (!type.equalsIgnoreCase("liudui")) {
if (type.equalsIgnoreCase("礼物")) {
String username = jsonObject.getString("昵称");
String userpng = jsonObject.getString("头像");
if(hashMap.get(userpng) != null){
username = hashMap.get(userpng);
}
String giftname = jsonObject.getString("礼物名称");
int amount = jsonObject.getInt("礼物增量");
if (amount >= 1){
liveRoomWatcher.callGift(new KuaiShouGift(username, giftname, amount));
}
}else if (type.equalsIgnoreCase("muyu")) {
String user = jsonObject.getString("user");
int zan = jsonObject.getInt("zan");
liveRoomWatcher.callLike(new KuaiShouLike(user, zan));
} else if (type.equalsIgnoreCase("点赞")) {
String user = jsonObject.getString("昵称");
int zan = jsonObject.getInt("点赞次数");
liveRoomWatcher.callLike(new KuaiShouLike(user, zan));
} else if (type.equalsIgnoreCase("fayan")) {
String nickname = jsonObject.getString("nickname");
String headurl = jsonObject.getString("headurl");
String nr = jsonObject.getString("nr");
liveRoomWatcher.callChat(new KuaiShouChat(nickname, nr));
} else if (type.equalsIgnoreCase("gift")) {
String username = jsonObject.getString("giftusername");
String giftpic = jsonObject.getString("giftpic");
hashMap.put(giftpic,username);
int amount = jsonObject.getInt("liwushuliang");
String giftPic2 = jsonObject.getString("liwutupian");
String nr = jsonObject.getString("giftnr");
String giftname = nr.split("个")[1];
} else if (type.equalsIgnoreCase("others")) {
String qian = jsonObject.getString("qian");
if(qian.contains("欢迎")){
String nr = jsonObject.getString("othernr");
String userName = nr.split(" 进入")[0];
liveRoomWatcher.callUser(new KuaiShouUser(userName));
}
}
}*/
}
@Override
public void onClose(int i, String s, boolean b) {
}
@Override
public void onError(Exception e) {
e.printStackTrace();
}
private void sendVerify() {
JSONObject jsonObject = new JSONObject();
jsonObject.put("type", "register");
KSWebSocketClient.this.send(jsonObject.toString().getBytes());
}
public void send() {
new BukkitRunnable() {
@Override
public void run() {
if (!KSWebSocketClient.this.isOpen()) {
// Bukkit.getConsoleSender().sendMessage("[直播互动] 心跳检测失败,已自动重连.");
// restartReconnectionTimer();
cancel();
return;
}
JSONObject jsonObject = new JSONObject();
jsonObject.put("type", "heartbeat");
KSWebSocketClient.this.send(jsonObject.toString().getBytes());
}
}.runTaskTimerAsynchronously(Main.plugin, 0L, 10 * 20L);
}
private boolean forcedClose = false;
private volatile boolean isReconnecting = false;
public void setForcedClose(boolean forcedClose) {
this.forcedClose = forcedClose;
}
private void restartReconnectionTimer() {
new BukkitRunnable() {
private int i = 0;
@Override
public void run() {
try {
if (reconnectBlocking()) {
cancel();
isReconnecting = false;
return;
}
if (i >= 100) {
if (player != null) {
player.sendMessage("§c[系统]§a直播间自动重连失败...");
}
cancel();
return;
}
i++;
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}.runTaskTimerAsynchronously(Main.plugin, 0L, 5L);
}
}

View File

@ -0,0 +1,63 @@
package com.io.yutian.livemutually.manager;
import cn.hamster3.cdapi.CDTimeAPI;
import com.io.yutian.livemutually.liveroom.KSLiveRoomClient;
import com.io.yutian.mclive.*;
import com.io.yutian.verify.AESUtil;
import org.bukkit.entity.Player;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Pattern;
public class KSLiveRoomManager {
public static Map<Player, KSLiveRoomClient> liveRoomClientMap = new HashMap<>();
public static void connect(Player player, String id) {
if (isConnected(player)) {
disconnect(player);
return;
}
if(!Pattern.matches("[a-zA-Z0-9_]+", id)) {
return;
}
String pluginName = Main.configYml.getGameMode();
if (AESUtil.isVerifyCheck(player,pluginName,id)) {
return;
}
CDTimeAPI.setPlayerCD(player.getUniqueId(),"link_live_Cd",1000 * 6);
KSLiveRoomClient liveRoomClient = new KSLiveRoomClient(id);
try {
liveRoomClient.openRoom(player);
} catch (Exception e) {
e.printStackTrace();
}
liveRoomClientMap.put(player, liveRoomClient);
if (player != null) {
player.sendMessage("§c[系统]§a您的直播间: §e" + id + " §a已连接...");
}
}
public static KSLiveRoomClient getLiveRoomClient(Player player) {
return liveRoomClientMap.get(player);
}
public static boolean isConnected(Player player) {
KSLiveRoomClient liveRoomClient = getLiveRoomClient(player);
return liveRoomClient != null && liveRoomClient.getSocketClient() != null && liveRoomClient.getSocketClient().isOpen();
}
public static void disconnect(Player player) {
if (!isConnected(player)) {
return;
}
KSLiveRoomClient liveRoomClient = getLiveRoomClient(player);
liveRoomClient.close();
if (player != null) {
player.sendMessage("§c[系统]§b已断开直播间连接...");
}
liveRoomClientMap.remove(player);
}
}

View File

@ -0,0 +1,58 @@
package com.io.yutian.livemutually.wss;
import com.io.yutian.livemutually.liveroom.LiveRoomWatcher;
import com.io.yutian.mclive.Main;
import com.io.yutian.mclive.event.LiveChatEvents;
import com.io.yutian.mclive.event.LiveEnterEvents;
import com.io.yutian.mclive.event.LiveGiftEvents;
import com.io.yutian.mclive.event.LiveLikeEvents;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
public class KSAPILiveRoomWatcher extends LiveRoomWatcher {
private Player player;
public KSAPILiveRoomWatcher(Player player) {
this.player = player;
onChat(chat -> {
Bukkit.getScheduler().runTask(Main.plugin, ()-> {
LiveChatEvents event = new LiveChatEvents(player, chat.user(), chat.content());
Bukkit.getPluginManager().callEvent(event);
});
});
onUser(user -> {
Bukkit.getScheduler().runTask(Main.plugin, ()-> {
LiveEnterEvents event = new LiveEnterEvents(player, user);
Bukkit.getPluginManager().callEvent(event);
});
});
onLike(like -> {
Bukkit.getScheduler().runTask(Main.plugin, ()-> {
LiveLikeEvents event = new LiveLikeEvents(player, like.user(), like.count());
Bukkit.getPluginManager().callEvent(event);
});
});
onGift(gift -> {
String giftName = gift.name();
long count = gift.count();
String count_color = "§a";
if (count >= 1314) {
count_color = "§c";
} else if (count >= 520) {
count_color = "§d";
} else if (count >= 188) {
count_color = "§e";
} else if (count >= 66) {
count_color = "§b";
}
String message = "§6[日志 - 礼物触发] §f"+player.getName()+ " >>> " + gift.user().nickName() + " = §a" + giftName + " 数量: "+count_color + count;
Bukkit.getConsoleSender().sendMessage(message);
Bukkit.getScheduler().runTask(Main.plugin, () -> {
LiveGiftEvents event = new LiveGiftEvents(player, gift.user(), gift.name(), gift.count());
Bukkit.getPluginManager().callEvent(event);
});
});
}
}

View File

@ -0,0 +1,157 @@
package com.io.yutian.livemutually.wss;
import com.io.yutian.livemutually.liveroom.KSLiveRoomClient;
import com.io.yutian.livemutually.liveroom.LiveRoomWatcher;
import com.io.yutian.livemutually.liveroom.User;
import com.io.yutian.mclive.Main;
import json.JSONObject;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.scheduler.BukkitRunnable;
import org.java_websocket.client.WebSocketClient;
import org.java_websocket.drafts.Draft_6455;
import org.java_websocket.handshake.ServerHandshake;
import java.net.URI;
import java.util.HashMap;
import java.util.Map;
public class KSWebSocketClient extends WebSocketClient {
private Player player;
private LiveRoomWatcher liveRoomWatcher;
public Player getPlayer() {
return player;
}
public KSWebSocketClient(Player player, URI uri, Map<String, String> httpHeaders) {
super(uri, new Draft_6455(), httpHeaders);
this.player = player;
this.liveRoomWatcher = new KSAPILiveRoomWatcher(player);
Bukkit.getConsoleSender().sendMessage("#WebSocketClient 已创建.");
}
@Override
public void onOpen(ServerHandshake serverHandshake) {
this.send();
}
public HashMap<String,String> hashMap = new HashMap<>();
@Override
public void onMessage(String s) {
//if(s.isEmpty()){return;}
Bukkit.getConsoleSender().sendMessage("TEST = "+s);
Bukkit.getConsoleSender().sendMessage("TEST = "+new JSONObject(s));
JSONObject jsonObject = new JSONObject(s);
/*String type = jsonObject.getString("type");
if (!type.equalsIgnoreCase("liudui")) {
if (type.equalsIgnoreCase("礼物")) {
String username = jsonObject.getString("昵称");
String userpng = jsonObject.getString("头像");
if(hashMap.get(userpng) != null){
username = hashMap.get(userpng);
}
String giftname = jsonObject.getString("礼物名称");
int amount = jsonObject.getInt("礼物增量");
if (amount >= 1){
liveRoomWatcher.callGift(new KuaiShouGift(username, giftname, amount));
}
}else if (type.equalsIgnoreCase("muyu")) {
String user = jsonObject.getString("user");
int zan = jsonObject.getInt("zan");
liveRoomWatcher.callLike(new KuaiShouLike(user, zan));
} else if (type.equalsIgnoreCase("点赞")) {
String user = jsonObject.getString("昵称");
int zan = jsonObject.getInt("点赞次数");
liveRoomWatcher.callLike(new KuaiShouLike(user, zan));
} else if (type.equalsIgnoreCase("fayan")) {
String nickname = jsonObject.getString("nickname");
String headurl = jsonObject.getString("headurl");
String nr = jsonObject.getString("nr");
liveRoomWatcher.callChat(new KuaiShouChat(nickname, nr));
} else if (type.equalsIgnoreCase("gift")) {
String username = jsonObject.getString("giftusername");
String giftpic = jsonObject.getString("giftpic");
hashMap.put(giftpic,username);
int amount = jsonObject.getInt("liwushuliang");
String giftPic2 = jsonObject.getString("liwutupian");
String nr = jsonObject.getString("giftnr");
String giftname = nr.split("")[1];
} else if (type.equalsIgnoreCase("others")) {
String qian = jsonObject.getString("qian");
if(qian.contains("欢迎")){
String nr = jsonObject.getString("othernr");
String userName = nr.split(" 进入")[0];
liveRoomWatcher.callUser(new KuaiShouUser(userName));
}
}
}*/
}
@Override
public void onClose(int i, String s, boolean b) {
}
@Override
public void onError(Exception e) {
e.printStackTrace();
}
private void sendVerify() {
JSONObject jsonObject = new JSONObject();
jsonObject.put("type", "register");
KSWebSocketClient.this.send(jsonObject.toString().getBytes());
}
public void send() {
new BukkitRunnable() {
@Override
public void run() {
if (!KSWebSocketClient.this.isOpen()) {
// Bukkit.getConsoleSender().sendMessage("[直播互动] 心跳检测失败,已自动重连.");
// restartReconnectionTimer();
cancel();
return;
}
JSONObject jsonObject = new JSONObject();
jsonObject.put("type", "heartbeat");
KSWebSocketClient.this.send(jsonObject.toString().getBytes());
}
}.runTaskTimerAsynchronously(Main.plugin, 0L, 10 * 20L);
}
private boolean forcedClose = false;
private volatile boolean isReconnecting = false;
public void setForcedClose(boolean forcedClose) {
this.forcedClose = forcedClose;
}
private void restartReconnectionTimer() {
new BukkitRunnable() {
private int i = 0;
@Override
public void run() {
try {
if (reconnectBlocking()) {
cancel();
isReconnecting = false;
return;
}
if (i >= 100) {
if (player != null) {
player.sendMessage("§c[系统]§a直播间自动重连失败...");
}
cancel();
return;
}
i++;
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}.runTaskTimerAsynchronously(Main.plugin, 0L, 5L);
}
}

View File

@ -0,0 +1,29 @@
package com.io.yutian.livemutually.wss;
import com.io.yutian.livemutually.liveroom.Chat;
import com.io.yutian.livemutually.liveroom.User;
public class KuaiShouChat implements Chat {
private String userName;
private String content;
private User user;
public KuaiShouChat(String userName, String content) {
this.userName = userName;
this.content = content;
this.user = new KuaiShouUser(userName);
}
@Override
public String content() {
return content;
}
@Override
public User user() {
return user;
}
}

View File

@ -0,0 +1,36 @@
package com.io.yutian.livemutually.wss;
import com.io.yutian.livemutually.liveroom.Gift;
import com.io.yutian.livemutually.liveroom.User;
public class KuaiShouGift implements Gift {
private String userName;
private String giftName;
private int amount;
private User user;
public KuaiShouGift(String userName, String giftName, int amount) {
this.userName = userName;
this.giftName = giftName;
this.amount = amount;
this.user = new KuaiShouUser(userName);
}
@Override
public User user() {
return user;
}
@Override
public String name() {
return giftName;
}
@Override
public long count() {
return amount;
}
}

View File

@ -0,0 +1,26 @@
package com.io.yutian.livemutually.wss;
import com.io.yutian.livemutually.liveroom.Like;
import com.io.yutian.livemutually.liveroom.User;
public class KuaiShouLike implements Like {
private User user;
private int count;
public KuaiShouLike(String nickName, int count) {
this.user = new KuaiShouUser(nickName);
this.count = count;
}
@Override
public User user() {
return user;
}
@Override
public long count() {
return count;
}
}

View File

@ -0,0 +1,23 @@
package com.io.yutian.livemutually.wss;
import com.io.yutian.livemutually.liveroom.User;
public class KuaiShouUser implements User {
private String nickName;
public KuaiShouUser(String nickName) {
this.nickName = nickName;
}
@Override
public String nickName() {
return nickName;
}
@Override
public String avatar() {
return null;
}
}

View File

@ -0,0 +1,76 @@
package com.io.yutian.mclive;
import org.bukkit.Bukkit;
import org.bukkit.configuration.file.FileConfiguration;
import java.util.HashMap;
public class ConfigYml {
private String GameMode;
private boolean MainDebug;
private HashMap<String, String> RoomId_Map = new HashMap<>();
public ConfigYml(FileConfiguration yml) {
this.MainDebug = yml.getBoolean("MainDebug");
this.GameMode = yml.getString("GameMode");
Bukkit.getConsoleSender().sendMessage("§b[整蛊MC直播] §7对接游戏: §e"+ this.GameMode);
if (yml.getConfigurationSection("LiveId") != null) {
HashMap<String, String> hashMap = new HashMap<>();
for (String playName : yml.getConfigurationSection("LiveId").getKeys(false)) {
String live_id = yml.getString("LiveId." + playName);
hashMap.put(playName, live_id);
Bukkit.getConsoleSender().sendMessage("§b[整蛊MC直播] §7主播: §e"+ playName + " - " + live_id);
}
this.RoomId_Map = hashMap;
}
int room_size = this.RoomId_Map.size();
if (room_size >= 1) {
Bukkit.getConsoleSender().sendMessage("§b[整蛊MC直播] §7当前模式: §a单人直播");
} else {
Bukkit.getConsoleSender().sendMessage("§b[整蛊MC直播] §7当前模式: §c尚未设置ID");
}
}
public void ReloadConfig() {
Main.plugin.reloadConfig();
Main.plugin.saveConfig();
Main.configYml = new ConfigYml(Main.plugin.getConfig());
}
public void SaveConfigYml() {
FileConfiguration yml = Main.plugin.getConfig();
yml.set("LiveId", null);
for (String playName : getRoomId_Map().keySet()) {
yml.set("LiveId." + playName, getRoomId(playName));
}
Main.plugin.saveConfig();
}
public boolean isMainDebug() {return MainDebug;}
public void setMainDebug(boolean butt) {
FileConfiguration yml = Main.plugin.getConfig();
yml.set("MainDebug",butt);
Main.plugin.saveConfig();
MainDebug = butt;
}
public String getGameMode() {
return this.GameMode;
}
public String getRoomId(String a) {
if (this.RoomId_Map.get(a) != null) {
return getRoomId_Map().get(a);
}
return null;
}
public HashMap<String, String> getRoomId_Map() {
return this.RoomId_Map;
}
public void setRoomId(String playName, String roomId) {
this.RoomId_Map.put(playName,roomId);
}
}

View File

@ -0,0 +1,41 @@
package com.io.yutian.mclive;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerKickEvent;
import org.bukkit.event.player.PlayerQuitEvent;
import org.bukkit.event.player.PlayerSwapHandItemsEvent;
public class LinkRoom implements Listener {
@EventHandler
public void onSwap(PlayerSwapHandItemsEvent e) {
Player p = e.getPlayer();
if (!Main.check_plugin) {
Bukkit.getConsoleSender().sendMessage("[系统提示] 当前房间未检测到游戏插件,请检查配置文件.");
p.sendMessage("§c[系统]§a游戏设置错误,未检测到游戏插件.");
return;
}
e.setCancelled(true);
if (p.isSneaking()) {
LiveAdminGui.OpenGui(p);
}
}
@EventHandler
public void onQuit(PlayerQuitEvent e){
Player player = e.getPlayer();
String playName = player.getName();
Main.configYml.SaveConfigYml();
}
@EventHandler
public void onKick(PlayerKickEvent e){
Player player = e.getPlayer();
String playName = player.getName();
Main.configYml.SaveConfigYml();
}
}

View File

@ -0,0 +1,231 @@
package com.io.yutian.mclive;
import cn.hamster3.cdapi.CDTimeAPI;
import com.io.yutian.livemutually.manager.KSLiveRoomManager;
import com.io.yutian.livemutually.wss.KSWebSocketClient;
import com.io.yutian.mclive.event.ZhuboAPI;
import com.io.yutian.verify.AESUtil;
import org.bukkit.*;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.inventory.InventoryClickEvent;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.ItemFlag;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
public class LiveAdminGui implements Listener {
public static String invTitle = "我的世界整蛊玩法操作界面";
@EventHandler
public void onclick(InventoryClickEvent e){
int rawSlot = e.getRawSlot();
Player player = (Player) e.getWhoClicked();
String playName = player.getName();
Inventory inv = e.getInventory();
if(e.getView().getTitle().equalsIgnoreCase(invTitle)){
e.setCancelled(true);
ItemStack item = e.getCurrentItem();
if(item != null && item.getType() == Material.AIR){
return;
}
if(rawSlot == 1){
player.closeInventory();
if(Main.configYml.getRoomId(playName) == null) {
player.sendMessage("§c[系统]§a请输入命令设置直播间ID: §e/mclive 授权号");
player.playSound(player.getLocation(), Sound.ENTITY_BLAZE_DEATH,1,1);
}else {
if (ZhuboAPI.isRoomisConnected(player)) {
return;
}
if(Main.LiveRoomClientList.size() >= 1) {
for (KSWebSocketClient socketClient : Main.LiveRoomClientList) {
if (socketClient.getPlayer().equals(player)) {
if (socketClient.isOpen()) {
socketClient.setForcedClose(true);
}
socketClient.close();
KSLiveRoomManager.liveRoomClientMap.remove(player);
}
}
}
String roomId = Main.configYml.getRoomId(playName);
KSLiveRoomManager.connect(player,roomId);
Location loc = player.getLocation();
loc.getWorld().playEffect(loc, Effect.MOBSPAWNER_FLAMES, 20);
player.playSound(player.getLocation(), Sound.BLOCK_NOTE_BLOCK_BASS,1,1);
}
}
if(rawSlot == 3){
player.closeInventory();
player.performCommand("livegift gui");
player.sendMessage("§c[系统]§a手持任意物品按住 §eSHIFT+Q §a打开界面");
player.playSound(player.getLocation(), Sound.BLOCK_COMPARATOR_CLICK,1,1);
}
if(rawSlot == 5){
player.closeInventory();
player.performCommand("gameedit");
player.playSound(player.getLocation(), Sound.BLOCK_COMPARATOR_CLICK,1,1);
}
if(rawSlot == 8){
player.closeInventory();
Bukkit.dispatchCommand(Bukkit.getConsoleSender(),"stop");
}
if(rawSlot == 18){
player.closeInventory();
Bukkit.dispatchCommand(Bukkit.getConsoleSender(),"mclive stop");
}
if(rawSlot == 20){
player.closeInventory();
if(Main.configYml.getRoomId(playName) == null) {
player.sendMessage("§c[系统]§a请输入命令设置直播间ID: §e/mclive 授权号");
player.playSound(player.getLocation(), Sound.ENTITY_BLAZE_DEATH,1,1);
}else {
String roomId = Main.configYml.getRoomId(playName);
if (AESUtil.isVerifyCheck(player,Main.configYml.getGameMode(),roomId)) {
player.playSound(player.getLocation(), Sound.ENTITY_BLAZE_DEATH,1,1);
return;
}
player.sendMessage("§c[系统]§a直播间授权通过,感谢您选择我们的产品.");
player.playSound(player.getLocation(), Sound.ENTITY_PLAYER_LEVELUP,1,1);
}
}
}
}
public static HashMap<Player,Integer> offline_zhubo = new HashMap<>();
public static void OpenGui(Player p){
Inventory inv = Bukkit.createInventory(null,27,invTitle);
inv.setItem(8,Stop_Server());
inv.setItem(1,Link_Room(p));
inv.setItem(3,Gift_BuChang());
inv.setItem(5,Games_Edit());
inv.setItem(18,Stop_LiveLink());
inv.setItem(20,test_Verify(p));
p.openInventory(inv);
}
public static ItemStack Stop_LiveLink(){
ItemStack item = new ItemStack(Material.FLINT);
ItemMeta meta = item.getItemMeta();
meta.setDisplayName("§d★ §e断开主播连接");
List<String> lore = new ArrayList<>();
lore.add("§c当出现一个礼物多个效果时点击");
lore.add("§b断开后需重新连接直播间");
lore.add(" ");
lore.add("§b★ §6鼠标点击 §7断开连接");
meta.setLore(lore);
meta.addItemFlags(ItemFlag.HIDE_ATTRIBUTES);
meta.setCustomModelData(55);
item.setItemMeta(meta);
return item;
}
public static ItemStack Stop_Server(){
ItemStack item = new ItemStack(Material.PAPER);
ItemMeta meta = item.getItemMeta();
meta.setDisplayName("§d★ §e重启服务器");
List<String> lore = new ArrayList<>();
lore.add("§c下播后请重启服务器");
lore.add(" ");
lore.add("§b★ §6鼠标点击 §7开始重启");
meta.setLore(lore);
meta.addItemFlags(ItemFlag.HIDE_ATTRIBUTES);
meta.setCustomModelData(55);
item.setItemMeta(meta);
return item;
}
public static ItemStack Gift_BuChang(){
ItemStack item = new ItemStack(Material.PAPER);
ItemMeta meta = item.getItemMeta();
meta.setDisplayName("§d★ §e礼物漏刷管理");
List<String> lore = new ArrayList<>();
lore.add("§c手动执行礼物效果");
lore.add(" ");
lore.add("§b★ §6鼠标点击 §7打开界面");
meta.setLore(lore);
meta.addItemFlags(ItemFlag.HIDE_ATTRIBUTES);
meta.setCustomModelData(11);
item.setItemMeta(meta);
return item;
}
public static ItemStack Games_Edit(){
ItemStack item = new ItemStack(Material.STONECUTTER);
ItemMeta meta = item.getItemMeta();
meta.setDisplayName("§d★ §e整蛊游戏设置");
List<String> lore = new ArrayList<>();
lore.add("§c设置部分游戏参数");
lore.add(" ");
lore.add("§b★ §6鼠标点击 §7打开界面");
meta.setLore(lore);
item.setItemMeta(meta);
return item;
}
public static ItemStack Link_Room(Player p){
ItemStack item = new ItemStack(Material.GOLDEN_SWORD);
ItemMeta meta = item.getItemMeta();
meta.setDisplayName("§d★ §a§l连接直播间");
if (ZhuboAPI.isRoomisConnected(p)) {
meta.setDisplayName("§d★ §c§l断开直播间");
}
List<String> lore = new ArrayList<>();
if (ZhuboAPI.isRoomisConnected(p)) {
String roomId = Main.configYml.getRoomId(p.getName());
lore.add("§d★ §c§l断开直播间");
lore.add(" ");
lore.add("§7您的直播间号: §a" + roomId);
lore.add(" ");
lore.add("§c★ §6鼠标点击 §7断开直播连接");
}else {
if (Main.configYml.getRoomId(p.getName()) == null) {
lore.add("§c建议开播前通过下方三项");
lore.add("§c测试后再开播并连接直播间");
lore.add("§7您的直播间号: §9尚未设置");
lore.add(" ");
lore.add("§b★ §6鼠标点击 §7设置直播间");
} else {
lore.add("§c建议开播前通过下方三项");
lore.add("§c测试后再开播并连接直播间");
String roomId = Main.configYml.getRoomId(p.getName());
lore.add("§7您的直播间号: §a" + roomId);
lore.add(" ");
lore.add("§b★ §6鼠标点击 §7开始连接");
}
}
meta.setLore(lore);
meta.addItemFlags(ItemFlag.HIDE_ATTRIBUTES);
item.setItemMeta(meta);
return item;
}
public static ItemStack test_Verify(Player p){
ItemStack item = new ItemStack(Material.IRON_INGOT);
ItemMeta meta = item.getItemMeta();
meta.setDisplayName("§d★ §e直播间授权验证");
List<String> lore = new ArrayList<>();
if(Main.configYml.getRoomId(p.getName()) == null) {
lore.add("§7您的直播间号: §9尚未设置");
lore.add(" ");
lore.add("§b★ §6鼠标点击 §7设置直播间");
}else{
String roomId = Main.configYml.getRoomId(p.getName());
lore.add("§7您的直播间号: §a"+roomId);
lore.add(" ");
lore.add("§b★ §6鼠标点击 §7开始测试");
}
meta.setLore(lore);
item.setItemMeta(meta);
return item;
}
}

View File

@ -0,0 +1,63 @@
package com.io.yutian.mclive;
import com.io.yutian.mclive.event.*;
import net.md_5.bungee.api.ChatMessageType;
import net.md_5.bungee.api.chat.TextComponent;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
public class LiveEvent implements Listener {
private ConfigYml configYml;
public LiveEvent(ConfigYml configYml){this.configYml = configYml;}
@EventHandler
public void onGift(LiveGiftEvents e){
if (Main.check_plugin) {
LiveAdminGui.offline_zhubo.put(e.getPlayer(), 1);
String type = "礼物";
String audience = e.getUser().nickName();
String gift_name = e.getName();
long gift_amount = e.getAmount();
// Bukkit.getConsoleSender().sendMessage("[直播互动 " + e.getPlayer().getName() + "] 类型: " + type + " 用户: " + audience + " 礼物: " + gift_name + "x" + gift_amount);
}
}
@EventHandler//关注
public void onMcLive(LiveFollowEvents e) {
LiveAdminGui.offline_zhubo.put(e.getPlayer(),1);
String type = "关注";
String audience = e.getUser().nickName();
Bukkit.getConsoleSender().sendMessage("[直播互动 "+e.getPlayer().getName()+"] 类型: " + type + " 用户: " + audience);
}
@EventHandler//信息
public void onMcLive(LiveChatEvents e) {
LiveAdminGui.offline_zhubo.put(e.getPlayer(),1);
String type = "信息";
String audience = e.getUser().nickName();
String message = e.getContent();
Bukkit.getConsoleSender().sendMessage("[直播互动 "+e.getPlayer().getName()+"] 类型: " + type + " 用户: " + audience + " 信息: " + message);
}
@EventHandler//点赞
public void onGuanzhu(LiveLikeEvents e) {
LiveAdminGui.offline_zhubo.put(e.getPlayer(),1);
String type = "点赞";
String audience = e.getUser().nickName();
long gift_amount = e.getCount();
// Bukkit.getConsoleSender().sendMessage("[直播互动 "+e.getPlayer().getName()+"] 类型: " + type + " 用户: " + audience + " 次数: " + gift_amount);
}
@EventHandler//进入
public void onMcLive(LiveEnterEvents e) {
Player player = e.getPlayer();
LiveAdminGui.offline_zhubo.put(e.getPlayer(),1);
String type = "进入";
String audience = e.getUser().nickName();
Bukkit.getConsoleSender().sendMessage("[直播互动 "+e.getPlayer().getName()+"] 类型: " + type + " 用户: " + audience);
player.spigot().sendMessage(ChatMessageType.ACTION_BAR,new TextComponent("§6" + Main.HideName(audience) + "来了"));
}
}

View File

@ -0,0 +1,165 @@
package com.io.yutian.mclive;
import cn.hamster3.cdapi.CDTimeAPI;
import com.io.yutian.livemutually.manager.KSLiveRoomManager;
import com.io.yutian.livemutually.wss.KSWebSocketClient;
import org.bukkit.Bukkit;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.entity.*;
import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.java.JavaPlugin;
import java.io.File;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.regex.Pattern;
public class Main extends JavaPlugin {
public static Main plugin;
public static boolean check_plugin = false;
public static ConfigYml configYml;
public static List<KSWebSocketClient> LiveRoomClientList = new ArrayList<>();
@Override
public void onEnable() {
plugin = this;
SendPluginsAuthorMessage(Bukkit.getConsoleSender());
saveDefaultConfig();
configYml = new ConfigYml(getConfig());
getServer().getPluginManager().registerEvents(new LiveAdminGui(),this);
getServer().getPluginManager().registerEvents(new LinkRoom(),this);
getServer().getPluginManager().registerEvents(new LiveEvent(configYml),this);
Bukkit.getConsoleSender().sendMessage("§b[整蛊MC直播] §7当前版本: §ev"+plugin.getDescription().getVersion());
Plugin plugin = getServer().getPluginManager().getPlugin(configYml.getGameMode());
if (plugin != null) {
check_plugin = true;
Bukkit.getConsoleSender().sendMessage("§b[整蛊MC直播] §a已激活游戏模式");
} else {
Bukkit.getConsoleSender().sendMessage("§b[整蛊MC直播] §c未检测到§a<"+configYml.getGameMode()+">§c游戏插件.");
}
}
@Override
public boolean onCommand(CommandSender sender, Command command, String Command, String[] args) {
if (Command.equalsIgnoreCase("mclive")) {
if (args.length == 0) {
sender.sendMessage("");
sender.sendMessage("§e------ ====== §6直播弹幕互动 §e====== ------");
if (sender instanceof Player) {
sender.sendMessage("§2/mclive §e[直播间ID] §f- §2设置直播间");
} else {
sender.sendMessage("§2/mclive §e[主播名] §2<直播间ID> §f- §2设置直播间");
}
sender.sendMessage("§2/mclive debug §f- §2开启调试模式");
sender.sendMessage("§2/mclive reload §f- §2重新载入配置文件");
sender.sendMessage("§e------ ====== §6直播弹幕互动 §e====== ------");
sender.sendMessage("");
return true;
}
if (args.length == 1 && args[0].equalsIgnoreCase("reload")) {
Main.configYml.ReloadConfig();
sender.sendMessage("§c[系统]§a配置文件已重载");
return true;
}
if (args.length == 1 && args[0].equalsIgnoreCase("stop")) {
KSLiveRoomManager.liveRoomClientMap.clear();
for (KSWebSocketClient socketClient : LiveRoomClientList) {
if (socketClient.isOpen()) {
socketClient.setForcedClose(true);
}
socketClient.close();
}
Bukkit.broadcastMessage("§c[系统]§a已断开所有主播的连接.");
return true;
}
if (args.length == 1 && args[0].equalsIgnoreCase("debug")) {
if (configYml.isMainDebug()) {
configYml.setMainDebug(false);
sender.sendMessage("§c[系统]§a调试模式: §b已关闭");
} else {
configYml.setMainDebug(true);
sender.sendMessage("§c[系统]§a调试模式: §a已启动");
}
return true;
}
if (args.length == 1 && sender instanceof Player player) {
String playName = player.getName();
String room_id = args[0];
if (!Pattern.matches("[a-zA-Z0-9_]+", room_id)) {
sender.sendMessage("§c[系统]§a您的抖音账号参数设置错误,正确示例: §e/mclive 123654789");
return true;
}
Main.configYml.setRoomId(playName, room_id);
Main.configYml.SaveConfigYml();
sender.sendMessage("§c[系统]§a直播间已设置 §e" + playName + " §a--> §e" + room_id);
return true;
}
if (args.length == 1 && args[0].equalsIgnoreCase("save")) {
Main.configYml.SaveConfigYml();
sender.sendMessage("§c[系统]§a配置文件已保存.");
return true;
}
if (args.length == 2) {
String playName = args[0];
String room_id = args[1];
Main.configYml.setRoomId(playName, room_id);
Main.configYml.SaveConfigYml();
sender.sendMessage("§c[系统]§a直播间已设置 §e" + playName + " §a--> §e" + room_id);
return true;
}
}
return false;
}
public static void SendPluginsAuthorMessage(CommandSender sender) {
sender.sendMessage(" ");
sender.sendMessage("§b ██ ████");
sender.sendMessage("§b ██");
sender.sendMessage("§b██░███▒ ████ ███ ███ ░████▒ ██");
sender.sendMessage("§b███████▒ ████ ██▒▒██ ░██████▒ ██");
sender.sendMessage("§b███ ███ ██ ▒████▒ ██▒ ▒██ ██");
sender.sendMessage("§b██░ ░██ ██ ████ ████████ ██");
sender.sendMessage("§b██ ██ ██ ▒██▒ ████████ ██");
sender.sendMessage("§b██░ ░██ ██ ████ ██ ██");
sender.sendMessage("§b███ ███ ██ ▒████▒ ███░ ▒█ ██▒");
sender.sendMessage("§b███████▒ ████████ ██▒▒██ ░███████ █████");
sender.sendMessage("§b██░███▒ ████████ ███ ███ ░█████▒ ░████");
sender.sendMessage("§b██");
sender.sendMessage("§b█ 弹幕互动引擎 由极光像素工作室提供技术支持!!");
sender.sendMessage("§b█ 如有使用问题可联系售后: §d"+getAuthorFile().getString("author"));
sender.sendMessage(""+getAuthorFile().getString("linemessage"));
}
public static FileConfiguration getAuthorFile(){
File ShopMenufile = new File("plugins/PluginMetrics", "config.yml");
return YamlConfiguration.loadConfiguration(ShopMenufile);
}
public static String HideName(String audience){
if(audience.length() <= 2){
return "**";
}
// 获取第一个和第二个字符
char firstChar = audience.charAt(0);
char lastChar = audience.charAt(audience.length() - 1);
// 构建屏蔽后的字符串
StringBuilder maskedString = new StringBuilder();
for (int i = 1; i < audience.length() - 1; i++) {
maskedString.append('*');
}
return String.valueOf(firstChar) + maskedString + lastChar;
}
public static String getTime(String format){
Date date = Calendar.getInstance().getTime();
SimpleDateFormat datatime = new SimpleDateFormat(format);
return datatime.format(date);
}
}

View File

@ -0,0 +1,44 @@
package com.io.yutian.mclive.event;
import com.io.yutian.livemutually.liveroom.User;
import org.bukkit.entity.Player;
import org.bukkit.event.Event;
import org.bukkit.event.HandlerList;
public class LiveChatEvents extends Event {
private static final HandlerList handlers = new HandlerList();
private Player player;
private User user;
private String content;
public LiveChatEvents(Player player, User user, String content) {
this.player = player;
this.user = user;
this.content = content;
}
public Player getPlayer() {
return player;
}
public User getUser() {
return user;
}
public String getContent() {
return content;
}
@Override
public HandlerList getHandlers() {
return handlers;
}
public final static HandlerList getHandlerList() {
return handlers;
}
}

View File

@ -0,0 +1,38 @@
package com.io.yutian.mclive.event;
import com.io.yutian.livemutually.liveroom.User;
import org.bukkit.entity.Player;
import org.bukkit.event.Event;
import org.bukkit.event.HandlerList;
public class LiveEnterEvents extends Event {
private static final HandlerList handlers = new HandlerList();
private Player player;
private User user;
public LiveEnterEvents(Player player, User user) {
this.player = player;
this.user = user;
}
public Player getPlayer() {
return player;
}
public User getUser() {
return user;
}
@Override
public HandlerList getHandlers() {
return handlers;
}
public final static HandlerList getHandlerList() {
return handlers;
}
}

View File

@ -0,0 +1,37 @@
package com.io.yutian.mclive.event;
import com.io.yutian.livemutually.liveroom.User;
import org.bukkit.entity.Player;
import org.bukkit.event.Event;
import org.bukkit.event.HandlerList;
public class LiveFollowEvents extends Event {
private static final HandlerList handlers = new HandlerList();
private Player player;
private User user;
public LiveFollowEvents(Player player, User user) {
this.player = player;
this.user = user;
}
public Player getPlayer() {
return player;
}
public User getUser() {
return user;
}
@Override
public HandlerList getHandlers() {
return handlers;
}
public final static HandlerList getHandlerList() {
return handlers;
}
}

View File

@ -0,0 +1,50 @@
package com.io.yutian.mclive.event;
import com.io.yutian.livemutually.liveroom.User;
import org.bukkit.entity.Player;
import org.bukkit.event.Event;
import org.bukkit.event.HandlerList;
public class LiveGiftEvents extends Event {
private static final HandlerList handlers = new HandlerList();
private Player player;
private User user;
private String name;
private long amount;
public LiveGiftEvents(Player player, User user, String name, long amount) {
this.player = player;
this.user = user;
this.name = name;
this.amount = amount;
}
public Player getPlayer() {
return player;
}
public User getUser() {
return user;
}
public String getName() {
return name;
}
public long getAmount() {
return amount;
}
@Override
public HandlerList getHandlers() {
return handlers;
}
public final static HandlerList getHandlerList() {
return handlers;
}
}

View File

@ -0,0 +1,43 @@
package com.io.yutian.mclive.event;
import com.io.yutian.livemutually.liveroom.User;
import org.bukkit.entity.Player;
import org.bukkit.event.Event;
import org.bukkit.event.HandlerList;
public class LiveLikeEvents extends Event {
private static final HandlerList handlers = new HandlerList();
private Player player;
private User user;
private long count;
public LiveLikeEvents(Player player, User user, long count) {
this.player = player;
this.user = user;
this.count = count;
}
public Player getPlayer() {
return player;
}
public User getUser() {
return user;
}
public long getCount() {
return count;
}
@Override
public HandlerList getHandlers() {
return handlers;
}
public final static HandlerList getHandlerList() {
return handlers;
}
}

View File

@ -0,0 +1,12 @@
package com.io.yutian.mclive.event;
import com.io.yutian.livemutually.manager.KSLiveRoomManager;
import com.io.yutian.mclive.Main;
import org.bukkit.entity.Player;
public abstract class ZhuboAPI {
// 获取主播的连接状态
public static boolean isRoomisConnected(Player zhubo){
return KSLiveRoomManager.isConnected(zhubo);
}
}

View File

@ -0,0 +1,95 @@
package com.io.yutian.verify;
import com.io.yutian.mclive.Main;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Base64;
public class AESUtil {
private static final String KEY_ALGORITHM = "AES";
private static final String DEFAULT_CIPHER_ALGORITHM = "AES/ECB/PKCS5Padding";
public static boolean isVerifyCheck(Player p, String pluginName, String roomId){
if(!Main.check_plugin){
return false;
}
PluginVerifyResult verifyResult = VerifyHandler.verify("127.0.0.1",pluginName,roomId);
if (!verifyResult.equals(PluginVerifyResult.VERIFY_SUCCESS)) {
if(verifyResult == PluginVerifyResult.USER_STATE_DISABLE){
Bukkit.getConsoleSender().sendMessage("[验证日志 - 拦截] "+p.getName()+" 的直播间授权已到期.");
p.sendMessage("§c[系统]§a验证尚未通过,您的直播间授权已到期.");
} else
if(verifyResult == PluginVerifyResult.FAIL_CODE){
Bukkit.getConsoleSender().sendMessage("[验证日志 - 拦截] "+p.getName()+" 的直播间尚未进行授权. "+roomId);
p.sendMessage("§c[系统]§a验证尚未通过,直播间§e<"+roomId+">§a尚未进行授权.");
} else
if(verifyResult == PluginVerifyResult.FAIL_TIMEOUT){
Bukkit.getConsoleSender().sendMessage("[验证日志 - 拦截] "+p.getName()+" 的本地网络有问题,无法连接验证服务器.");
p.sendMessage("§c[系统]§a验证尚未通过,您当前的网络环境有问题.");
} else {
Bukkit.getConsoleSender().sendMessage("[验证日志 - 拦截] "+p.getName()+" 的无法通过验证. "+verifyResult);
p.sendMessage("§c[系统]§a验证尚未通过,无法连接直播间,请联系管理员。§c§l#" + verifyResult);
}
return true;
}
return false;
}
public static String getAESRandomKey() {
SecureRandom random = new SecureRandom();
long randomKey = random.nextLong();
return String.valueOf(randomKey);
}
public static String encrypt(String content, String key) {
try {
Cipher cipher = Cipher.getInstance(DEFAULT_CIPHER_ALGORITHM);
byte[] byteContent = content.getBytes("utf-8");
cipher.init(Cipher.ENCRYPT_MODE, getSecretKey(key));
byte[] result = cipher.doFinal(byteContent);
return byte2Base64(result);
} catch (Exception ex) {
}
return null;
}
public static String decrypt(String content, String key) {
try {
Cipher cipher = Cipher.getInstance(DEFAULT_CIPHER_ALGORITHM);
cipher.init(Cipher.DECRYPT_MODE, getSecretKey(key));
byte[] result = cipher.doFinal(base642Byte(content));
return new String(result, "utf-8");
} catch (Exception ex) {
}
return null;
}
private static SecretKeySpec getSecretKey(final String key) {
try {
KeyGenerator kg = KeyGenerator.getInstance(KEY_ALGORITHM);
SecureRandom random = SecureRandom.getInstance("SHA1PRNG");
random.setSeed(key.getBytes());
kg.init(128, random);
SecretKey secretKey = kg.generateKey();
return new SecretKeySpec(secretKey.getEncoded(), KEY_ALGORITHM);
} catch (NoSuchAlgorithmException ex) {
}
return null;
}
private static String byte2Base64(byte[] bytes) {
return Base64.getEncoder().encodeToString(bytes);
}
private static byte[] base642Byte(String base64Key) {
return Base64.getDecoder().decode(base64Key);
}
}

View File

@ -0,0 +1,16 @@
package com.io.yutian.verify;
public enum PluginVerifyResult {
INVALID_DATA_MISS_PARAMETER,
VERIFY_SUCCESS,
FAIL_TIMEOUT,
FAIL_IP,
FAIL_CODE,
INVALID_DATA_PLUGIN,
PLUGIN_DISABLE,
USER_STATE_DISABLE,
UNABLE_CONNECT_SERVER,
UNKNOWN
}

View File

@ -0,0 +1,75 @@
package com.io.yutian.verify;
import json.JSONObject;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.ConnectException;
import java.net.SocketTimeoutException;
import java.net.URL;
import java.net.URLConnection;
public class VerifyHandler {
public static PluginVerifyResult verify(String ip, String pluginName, String sequenceCode) {
try {
if (ip == null) {
return PluginVerifyResult.UNKNOWN;
}
if (sequenceCode == null || sequenceCode.trim().isEmpty()) {
return PluginVerifyResult.FAIL_CODE;
}
String result = get(pluginName, sequenceCode);
PluginVerifyResult pluginVerifyResult = PluginVerifyResult.UNKNOWN;
JSONObject jsonObject1 = new JSONObject(result);
int code = jsonObject1.getInt("code");
if (code == 100) {
pluginVerifyResult = PluginVerifyResult.VERIFY_SUCCESS;
// APIVerifyHandler.handlerAPI();
} else if (code == -1) {
pluginVerifyResult = PluginVerifyResult.INVALID_DATA_PLUGIN;
} else if (code == -2) {
pluginVerifyResult = PluginVerifyResult.FAIL_CODE;
} else if (code == -100) {
pluginVerifyResult = PluginVerifyResult.USER_STATE_DISABLE;
}
return pluginVerifyResult;
} catch (Exception e) {
if (e instanceof SocketTimeoutException) {
return PluginVerifyResult.VERIFY_SUCCESS;
}
return PluginVerifyResult.UNKNOWN;
}
}
public static String getServerIp(){
String a = "47.";
String b = "111.";
String c = "169.";
String d = "142";
String e = "82";
String f = "81";
return a+b+c+d+":"+e+f;
}
private static String get(String plugin, String code) throws Exception {
String url = "/verify?plugin="+plugin+"&code="+code;
URL realUrl = new URL("http://"+getServerIp()+url);
URLConnection connection = realUrl.openConnection();
connection.setConnectTimeout(5000);
connection.setReadTimeout(15000);
connection.setRequestProperty("accept", "*/*");
connection.setRequestProperty("connection", "Keep-Alive");
connection.setRequestProperty("user-agent","Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
connection.connect();
BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream(), "utf-8"));
StringBuilder stringBuilder = new StringBuilder();
String line;
while ((line = in.readLine()) != null) {
stringBuilder.append(line);
}
if (in != null) {
in.close();
}
return stringBuilder.toString();
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,45 @@
package json;
/**
* The JSONException is thrown by the JSON.org classes when things are amiss.
*
* @author JSON.org
* @version 2015-12-09
*/
public class JSONException extends RuntimeException {
/** Serialization ID */
private static final long serialVersionUID = 0;
/**
* Constructs a JSONException with an explanatory message.
*
* @param message
* Detail about the reason for the exception.
*/
public JSONException(final String message) {
super(message);
}
/**
* Constructs a JSONException with an explanatory message and cause.
*
* @param message
* Detail about the reason for the exception.
* @param cause
* The cause.
*/
public JSONException(final String message, final Throwable cause) {
super(message, cause);
}
/**
* Constructs a new JSONException with the specified cause.
*
* @param cause
* The cause.
*/
public JSONException(final Throwable cause) {
super(cause.getMessage(), cause);
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,293 @@
package json;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import static java.lang.String.format;
/*
Copyright (c) 2002 JSON.org
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
The Software shall be used for Good, not Evil.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
/**
* A JSON Pointer is a simple query language defined for JSON documents by
* <a href="https://tools.ietf.org/html/rfc6901">RFC 6901</a>.
*
* In a nutshell, JSONPointer allows the user to navigate into a JSON document
* using strings, and retrieve targeted objects, like a simple form of XPATH.
* Path segments are separated by the '/' char, which signifies the root of
* the document when it appears as the first char of the string. Array
* elements are navigated using ordinals, counting from 0. JSONPointer strings
* may be extended to any arbitrary number of segments. If the navigation
* is successful, the matched item is returned. A matched item may be a
* JSONObject, a JSONArray, or a JSON value. If the JSONPointer string building
* fails, an appropriate exception is thrown. If the navigation fails to find
* a match, a JSONPointerException is thrown.
*
* @author JSON.org
* @version 2016-05-14
*/
public class JSONPointer {
// used for URL encoding and decoding
private static final String ENCODING = "utf-8";
/**
* This class allows the user to build a JSONPointer in steps, using
* exactly one segment in each step.
*/
public static class Builder {
// Segments for the eventual JSONPointer string
private final List<String> refTokens = new ArrayList<String>();
/**
* Creates a {@code JSONPointer} instance using the tokens previously set using the
* {@link #append(String)} method calls.
*/
public JSONPointer build() {
return new JSONPointer(this.refTokens);
}
/**
* Adds an arbitrary token to the list of reference tokens. It can be any non-null value.
*
* Unlike in the case of JSON string or URI fragment representation of JSON pointers, the
* argument of this method MUST NOT be escaped. If you want to query the property called
* {@code "a~b"} then you should simply pass the {@code "a~b"} string as-is, there is no
* need to escape it as {@code "a~0b"}.
*
* @param token the new token to be appended to the list
* @return {@code this}
* @throws NullPointerException if {@code token} is null
*/
public Builder append(String token) {
if (token == null) {
throw new NullPointerException("token cannot be null");
}
this.refTokens.add(token);
return this;
}
/**
* Adds an integer to the reference token list. Although not necessarily, mostly this token will
* denote an array index.
*
* @param arrayIndex the array index to be added to the token list
* @return {@code this}
*/
public Builder append(int arrayIndex) {
this.refTokens.add(String.valueOf(arrayIndex));
return this;
}
}
/**
* Static factory method for {@link Builder}. Example usage:
*
* <pre><code>
* JSONPointer pointer = JSONPointer.builder()
* .append("obj")
* .append("other~key").append("another/key")
* .append("\"")
* .append(0)
* .build();
* </code></pre>
*
* @return a builder instance which can be used to construct a {@code JSONPointer} instance by chained
* {@link Builder#append(String)} calls.
*/
public static Builder builder() {
return new Builder();
}
// Segments for the JSONPointer string
private final List<String> refTokens;
/**
* Pre-parses and initializes a new {@code JSONPointer} instance. If you want to
* evaluate the same JSON Pointer on different JSON documents then it is recommended
* to keep the {@code JSONPointer} instances due to performance considerations.
*
* @param pointer the JSON String or URI Fragment representation of the JSON pointer.
* @throws IllegalArgumentException if {@code pointer} is not a valid JSON pointer
*/
public JSONPointer(final String pointer) {
if (pointer == null) {
throw new NullPointerException("pointer cannot be null");
}
if (pointer.isEmpty() || pointer.equals("#")) {
this.refTokens = Collections.emptyList();
return;
}
String refs;
if (pointer.startsWith("#/")) {
refs = pointer.substring(2);
try {
refs = URLDecoder.decode(refs, ENCODING);
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
} else if (pointer.startsWith("/")) {
refs = pointer.substring(1);
} else {
throw new IllegalArgumentException("a JSON pointer should start with '/' or '#/'");
}
this.refTokens = new ArrayList<String>();
int slashIdx = -1;
int prevSlashIdx = 0;
do {
prevSlashIdx = slashIdx + 1;
slashIdx = refs.indexOf('/', prevSlashIdx);
if(prevSlashIdx == slashIdx || prevSlashIdx == refs.length()) {
// found 2 slashes in a row ( obj//next )
// or single slash at the end of a string ( obj/test/ )
this.refTokens.add("");
} else if (slashIdx >= 0) {
final String token = refs.substring(prevSlashIdx, slashIdx);
this.refTokens.add(unescape(token));
} else {
// last item after separator, or no separator at all.
final String token = refs.substring(prevSlashIdx);
this.refTokens.add(unescape(token));
}
} while (slashIdx >= 0);
// using split does not take into account consecutive separators or "ending nulls"
//for (String token : refs.split("/")) {
// this.refTokens.add(unescape(token));
//}
}
public JSONPointer(List<String> refTokens) {
this.refTokens = new ArrayList<String>(refTokens);
}
private static String unescape(String token) {
return token.replace("~1", "/").replace("~0", "~")
.replace("\\\"", "\"")
.replace("\\\\", "\\");
}
/**
* Evaluates this JSON Pointer on the given {@code document}. The {@code document}
* is usually a {@link JSONObject} or a {@link JSONArray} instance, but the empty
* JSON Pointer ({@code ""}) can be evaluated on any JSON values and in such case the
* returned value will be {@code document} itself.
*
* @param document the JSON document which should be the subject of querying.
* @return the result of the evaluation
* @throws JSONPointerException if an error occurs during evaluation
*/
public Object queryFrom(Object document) throws JSONPointerException {
if (this.refTokens.isEmpty()) {
return document;
}
Object current = document;
for (String token : this.refTokens) {
if (current instanceof JSONObject) {
current = ((JSONObject) current).opt(unescape(token));
} else if (current instanceof JSONArray) {
current = readByIndexToken(current, token);
} else {
throw new JSONPointerException(format(
"value [%s] is not an array or object therefore its key %s cannot be resolved", current,
token));
}
}
return current;
}
/**
* Matches a JSONArray element by ordinal position
* @param current the JSONArray to be evaluated
* @param indexToken the array index in string form
* @return the matched object. If no matching item is found a
* @throws JSONPointerException is thrown if the index is out of bounds
*/
private static Object readByIndexToken(Object current, String indexToken) throws JSONPointerException {
try {
int index = Integer.parseInt(indexToken);
JSONArray currentArr = (JSONArray) current;
if (index >= currentArr.length()) {
throw new JSONPointerException(format("index %s is out of bounds - the array has %d elements", indexToken,
Integer.valueOf(currentArr.length())));
}
try {
return currentArr.get(index);
} catch (JSONException e) {
throw new JSONPointerException("Error reading value at index position " + index, e);
}
} catch (NumberFormatException e) {
throw new JSONPointerException(format("%s is not an array index", indexToken), e);
}
}
/**
* Returns a string representing the JSONPointer path value using string
* representation
*/
@Override
public String toString() {
StringBuilder rval = new StringBuilder("");
for (String token: this.refTokens) {
rval.append('/').append(escape(token));
}
return rval.toString();
}
/**
* Escapes path segment values to an unambiguous form.
* The escape char to be inserted is '~'. The chars to be escaped
* are ~, which maps to ~0, and /, which maps to ~1. Backslashes
* and double quote chars are also escaped.
* @param token the JSONPointer segment value to be escaped
* @return the escaped value for the token
*/
private static String escape(String token) {
return token.replace("~", "~0")
.replace("/", "~1")
.replace("\\", "\\\\")
.replace("\"", "\\\"");
}
/**
* Returns a string representing the JSONPointer path value using URI
* fragment identifier representation
*/
public String toURIFragment() {
try {
StringBuilder rval = new StringBuilder("#");
for (String token : this.refTokens) {
rval.append('/').append(URLEncoder.encode(token, ENCODING));
}
return rval.toString();
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
}
}

View File

@ -0,0 +1,45 @@
package json;
/*
Copyright (c) 2002 JSON.org
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
The Software shall be used for Good, not Evil.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
/**
* The JSONPointerException is thrown by {@link JSONPointer} if an error occurs
* during evaluating a pointer.
*
* @author JSON.org
* @version 2016-05-13
*/
public class JSONPointerException extends JSONException {
private static final long serialVersionUID = 8872944667561856751L;
public JSONPointerException(String message) {
super(message);
}
public JSONPointerException(String message, Throwable cause) {
super(message, cause);
}
}

View File

@ -0,0 +1,43 @@
package json;
/*
Copyright (c) 2018 JSON.org
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
The Software shall be used for Good, not Evil.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
@Documented
@Retention(RUNTIME)
@Target({METHOD})
/**
* Use this annotation on a getter method to override the Bean name
* parser for Bean -&gt; JSONObject mapping. If this annotation is
* present at any level in the class hierarchy, then the method will
* not be serialized from the bean into the JSONObject.
*/
public @interface JSONPropertyIgnore { }

View File

@ -0,0 +1,47 @@
package json;
/*
Copyright (c) 2018 JSON.org
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
The Software shall be used for Good, not Evil.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
@Documented
@Retention(RUNTIME)
@Target({METHOD})
/**
* Use this annotation on a getter method to override the Bean name
* parser for Bean -&gt; JSONObject mapping. A value set to empty string <code>""</code>
* will have the Bean parser fall back to the default field name processing.
*/
public @interface JSONPropertyName {
/**
* @return The name of the property as to be used in the JSON Object.
*/
String value();
}

View File

@ -0,0 +1,18 @@
package json;
/**
* The <code>JSONString</code> interface allows a <code>toJSONString()</code>
* method so that a class can change the behavior of
* <code>JSONObject.toString()</code>, <code>JSONArray.toString()</code>,
* and <code>JSONWriter.value(</code>Object<code>)</code>. The
* <code>toJSONString</code> method will be used instead of the default behavior
* of using the Object's <code>toString()</code> method and quoting the result.
*/
public interface JSONString {
/**
* The <code>toJSONString</code> method allows a class to produce its own JSON
* serialization.
*
* @return A strictly syntactically correct JSON text.
*/
public String toJSONString();
}

View File

@ -0,0 +1,79 @@
package json;
/*
Copyright (c) 2006 JSON.org
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
The Software shall be used for Good, not Evil.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
import java.io.StringWriter;
/**
* JSONStringer provides a quick and convenient way of producing JSON text.
* The texts produced strictly conform to JSON syntax rules. No whitespace is
* added, so the results are ready for transmission or storage. Each instance of
* JSONStringer can produce one JSON text.
* <p>
* A JSONStringer instance provides a <code>value</code> method for appending
* values to the
* text, and a <code>key</code>
* method for adding keys before values in objects. There are <code>array</code>
* and <code>endArray</code> methods that make and bound array values, and
* <code>object</code> and <code>endObject</code> methods which make and bound
* object values. All of these methods return the JSONWriter instance,
* permitting cascade style. For example, <pre>
* myString = new JSONStringer()
* .object()
* .key("JSON")
* .value("Hello, World!")
* .endObject()
* .toString();</pre> which produces the string <pre>
* {"JSON":"Hello, World!"}</pre>
* <p>
* The first method called must be <code>array</code> or <code>object</code>.
* There are no methods for adding commas or colons. JSONStringer adds them for
* you. Objects and arrays can be nested up to 20 levels deep.
* <p>
* This can sometimes be easier than using a JSONObject to build a string.
* @author JSON.org
* @version 2015-12-09
*/
public class JSONStringer extends JSONWriter {
/**
* Make a fresh JSONStringer. It can be used to build one JSON text.
*/
public JSONStringer() {
super(new StringWriter());
}
/**
* Return the JSON text. This method is used to obtain the product of the
* JSONStringer instance. It will return <code>null</code> if there was a
* problem in the construction of the JSON text (such as the calls to
* <code>array</code> were not properly balanced with calls to
* <code>endArray</code>).
* @return The JSON text.
*/
@Override
public String toString() {
return this.mode == 'd' ? this.writer.toString() : null;
}
}

View File

@ -0,0 +1,526 @@
package json;
import java.io.*;
/*
Copyright (c) 2002 JSON.org
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
The Software shall be used for Good, not Evil.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
/**
* A JSONTokener takes a source string and extracts characters and tokens from
* it. It is used by the JSONObject and JSONArray constructors to parse
* JSON source strings.
* @author JSON.org
* @version 2014-05-03
*/
public class JSONTokener {
/** current read character position on the current line. */
private long character;
/** flag to indicate if the end of the input has been found. */
private boolean eof;
/** current read index of the input. */
private long index;
/** current line of the input. */
private long line;
/** previous character read from the input. */
private char previous;
/** Reader for the input. */
private final Reader reader;
/** flag to indicate that a previous character was requested. */
private boolean usePrevious;
/** the number of characters read in the previous line. */
private long characterPreviousLine;
/**
* Construct a JSONTokener from a Reader. The caller must close the Reader.
*
* @param reader A reader.
*/
public JSONTokener(Reader reader) {
this.reader = reader.markSupported()
? reader
: new BufferedReader(reader);
this.eof = false;
this.usePrevious = false;
this.previous = 0;
this.index = 0;
this.character = 1;
this.characterPreviousLine = 0;
this.line = 1;
}
/**
* Construct a JSONTokener from an InputStream. The caller must close the input stream.
* @param inputStream The source.
*/
public JSONTokener(InputStream inputStream) {
this(new InputStreamReader(inputStream));
}
/**
* Construct a JSONTokener from a string.
*
* @param s A source string.
*/
public JSONTokener(String s) {
this(new StringReader(s));
}
/**
* Back up one character. This provides a sort of lookahead capability,
* so that you can test for a digit or letter before attempting to parse
* the next number or identifier.
* @throws JSONException Thrown if trying to step back more than 1 step
* or if already at the start of the string
*/
public void back() throws JSONException {
if (this.usePrevious || this.index <= 0) {
throw new JSONException("Stepping back two steps is not supported");
}
this.decrementIndexes();
this.usePrevious = true;
this.eof = false;
}
/**
* Decrements the indexes for the {@link #back()} method based on the previous character read.
*/
private void decrementIndexes() {
this.index--;
if(this.previous=='\r' || this.previous == '\n') {
this.line--;
this.character=this.characterPreviousLine ;
} else if(this.character > 0){
this.character--;
}
}
/**
* Get the hex value of a character (base16).
* @param c A character between '0' and '9' or between 'A' and 'F' or
* between 'a' and 'f'.
* @return An int between 0 and 15, or -1 if c was not a hex digit.
*/
public static int dehexchar(char c) {
if (c >= '0' && c <= '9') {
return c - '0';
}
if (c >= 'A' && c <= 'F') {
return c - ('A' - 10);
}
if (c >= 'a' && c <= 'f') {
return c - ('a' - 10);
}
return -1;
}
/**
* Checks if the end of the input has been reached.
*
* @return true if at the end of the file and we didn't step back
*/
public boolean end() {
return this.eof && !this.usePrevious;
}
/**
* Determine if the source string still contains characters that next()
* can consume.
* @return true if not yet at the end of the source.
* @throws JSONException thrown if there is an error stepping forward
* or backward while checking for more data.
*/
public boolean more() throws JSONException {
if(this.usePrevious) {
return true;
}
try {
this.reader.mark(1);
} catch (IOException e) {
throw new JSONException("Unable to preserve stream position", e);
}
try {
// -1 is EOF, but next() can not consume the null character '\0'
if(this.reader.read() <= 0) {
this.eof = true;
return false;
}
this.reader.reset();
} catch (IOException e) {
throw new JSONException("Unable to read the next character from the stream", e);
}
return true;
}
/**
* Get the next character in the source string.
*
* @return The next character, or 0 if past the end of the source string.
* @throws JSONException Thrown if there is an error reading the source string.
*/
public char next() throws JSONException {
int c;
if (this.usePrevious) {
this.usePrevious = false;
c = this.previous;
} else {
try {
c = this.reader.read();
} catch (IOException exception) {
throw new JSONException(exception);
}
}
if (c <= 0) { // End of stream
this.eof = true;
return 0;
}
this.incrementIndexes(c);
this.previous = (char) c;
return this.previous;
}
/**
* Increments the internal indexes according to the previous character
* read and the character passed as the current character.
* @param c the current character read.
*/
private void incrementIndexes(int c) {
if(c > 0) {
this.index++;
if(c=='\r') {
this.line++;
this.characterPreviousLine = this.character;
this.character=0;
}else if (c=='\n') {
if(this.previous != '\r') {
this.line++;
this.characterPreviousLine = this.character;
}
this.character=0;
} else {
this.character++;
}
}
}
/**
* Consume the next character, and check that it matches a specified
* character.
* @param c The character to match.
* @return The character.
* @throws JSONException if the character does not match.
*/
public char next(char c) throws JSONException {
char n = this.next();
if (n != c) {
if(n > 0) {
throw this.syntaxError("Expected '" + c + "' and instead saw '" +
n + "'");
}
throw this.syntaxError("Expected '" + c + "' and instead saw ''");
}
return n;
}
/**
* Get the next n characters.
*
* @param n The number of characters to take.
* @return A string of n characters.
* @throws JSONException
* Substring bounds error if there are not
* n characters remaining in the source string.
*/
public String next(int n) throws JSONException {
if (n == 0) {
return "";
}
char[] chars = new char[n];
int pos = 0;
while (pos < n) {
chars[pos] = this.next();
if (this.end()) {
throw this.syntaxError("Substring bounds error");
}
pos += 1;
}
return new String(chars);
}
/**
* Get the next char in the string, skipping whitespace.
* @throws JSONException Thrown if there is an error reading the source string.
* @return A character, or 0 if there are no more characters.
*/
public char nextClean() throws JSONException {
for (;;) {
char c = this.next();
if (c == 0 || c > ' ') {
return c;
}
}
}
/**
* Return the characters up to the next close quote character.
* Backslash processing is done. The formal JSON format does not
* allow strings in single quotes, but an implementation is allowed to
* accept them.
* @param quote The quoting character, either
* <code>"</code>&nbsp;<small>(double quote)</small> or
* <code>'</code>&nbsp;<small>(single quote)</small>.
* @return A String.
* @throws JSONException Unterminated string.
*/
public String nextString(char quote) throws JSONException {
char c;
StringBuilder sb = new StringBuilder();
for (;;) {
c = this.next();
switch (c) {
case 0:
case '\n':
case '\r':
throw this.syntaxError("Unterminated string");
case '\\':
c = this.next();
switch (c) {
case 'b':
sb.append('\b');
break;
case 't':
sb.append('\t');
break;
case 'n':
sb.append('\n');
break;
case 'f':
sb.append('\f');
break;
case 'r':
sb.append('\r');
break;
case 'u':
try {
sb.append((char)Integer.parseInt(this.next(4), 16));
} catch (NumberFormatException e) {
throw this.syntaxError("Illegal escape.", e);
}
break;
case '"':
case '\'':
case '\\':
case '/':
sb.append(c);
break;
default:
throw this.syntaxError("Illegal escape.");
}
break;
default:
if (c == quote) {
return sb.toString();
}
sb.append(c);
}
}
}
/**
* Get the text up but not including the specified character or the
* end of line, whichever comes first.
* @param delimiter A delimiter character.
* @return A string.
* @throws JSONException Thrown if there is an error while searching
* for the delimiter
*/
public String nextTo(char delimiter) throws JSONException {
StringBuilder sb = new StringBuilder();
for (;;) {
char c = this.next();
if (c == delimiter || c == 0 || c == '\n' || c == '\r') {
if (c != 0) {
this.back();
}
return sb.toString().trim();
}
sb.append(c);
}
}
/**
* Get the text up but not including one of the specified delimiter
* characters or the end of line, whichever comes first.
* @param delimiters A set of delimiter characters.
* @return A string, trimmed.
* @throws JSONException Thrown if there is an error while searching
* for the delimiter
*/
public String nextTo(String delimiters) throws JSONException {
char c;
StringBuilder sb = new StringBuilder();
for (;;) {
c = this.next();
if (delimiters.indexOf(c) >= 0 || c == 0 ||
c == '\n' || c == '\r') {
if (c != 0) {
this.back();
}
return sb.toString().trim();
}
sb.append(c);
}
}
/**
* Get the next value. The value can be a Boolean, Double, Integer,
* JSONArray, JSONObject, Long, or String, or the JSONObject.NULL object.
* @throws JSONException If syntax error.
*
* @return An object.
*/
public Object nextValue() throws JSONException {
char c = this.nextClean();
String string;
switch (c) {
case '"':
case '\'':
return this.nextString(c);
case '{':
this.back();
return new JSONObject(this);
case '[':
this.back();
return new JSONArray(this);
}
/*
* Handle unquoted text. This could be the values true, false, or
* null, or it can be a number. An implementation (such as this one)
* is allowed to also accept non-standard forms.
*
* Accumulate characters until we reach the end of the text or a
* formatting character.
*/
StringBuilder sb = new StringBuilder();
while (c >= ' ' && ",:]}/\\\"[{;=#".indexOf(c) < 0) {
sb.append(c);
c = this.next();
}
if (!this.eof) {
this.back();
}
string = sb.toString().trim();
if ("".equals(string)) {
throw this.syntaxError("Missing value");
}
return JSONObject.stringToValue(string);
}
/**
* Skip characters until the next character is the requested character.
* If the requested character is not found, no characters are skipped.
* @param to A character to skip to.
* @return The requested character, or zero if the requested character
* is not found.
* @throws JSONException Thrown if there is an error while searching
* for the to character
*/
public char skipTo(char to) throws JSONException {
char c;
try {
long startIndex = this.index;
long startCharacter = this.character;
long startLine = this.line;
this.reader.mark(1000000);
do {
c = this.next();
if (c == 0) {
// in some readers, reset() may throw an exception if
// the remaining portion of the input is greater than
// the mark size (1,000,000 above).
this.reader.reset();
this.index = startIndex;
this.character = startCharacter;
this.line = startLine;
return 0;
}
} while (c != to);
this.reader.mark(1);
} catch (IOException exception) {
throw new JSONException(exception);
}
this.back();
return c;
}
/**
* Make a JSONException to signal a syntax error.
*
* @param message The error message.
* @return A JSONException object, suitable for throwing
*/
public JSONException syntaxError(String message) {
return new JSONException(message + this.toString());
}
/**
* Make a JSONException to signal a syntax error.
*
* @param message The error message.
* @param causedBy The throwable that caused the error.
* @return A JSONException object, suitable for throwing
*/
public JSONException syntaxError(String message, Throwable causedBy) {
return new JSONException(message + this.toString(), causedBy);
}
/**
* Make a printable string of this JSONTokener.
*
* @return " at {index} [character {character} line {line}]"
*/
@Override
public String toString() {
return " at " + this.index + " [character " + this.character + " line " +
this.line + "]";
}
}

View File

@ -0,0 +1,413 @@
package json;
import java.io.IOException;
import java.util.Collection;
import java.util.Map;
/*
Copyright (c) 2006 JSON.org
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
The Software shall be used for Good, not Evil.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
/**
* JSONWriter provides a quick and convenient way of producing JSON text.
* The texts produced strictly conform to JSON syntax rules. No whitespace is
* added, so the results are ready for transmission or storage. Each instance of
* JSONWriter can produce one JSON text.
* <p>
* A JSONWriter instance provides a <code>value</code> method for appending
* values to the
* text, and a <code>key</code>
* method for adding keys before values in objects. There are <code>array</code>
* and <code>endArray</code> methods that make and bound array values, and
* <code>object</code> and <code>endObject</code> methods which make and bound
* object values. All of these methods return the JSONWriter instance,
* permitting a cascade style. For example, <pre>
* new JSONWriter(myWriter)
* .object()
* .key("JSON")
* .value("Hello, World!")
* .endObject();</pre> which writes <pre>
* {"JSON":"Hello, World!"}</pre>
* <p>
* The first method called must be <code>array</code> or <code>object</code>.
* There are no methods for adding commas or colons. JSONWriter adds them for
* you. Objects and arrays can be nested up to 200 levels deep.
* <p>
* This can sometimes be easier than using a JSONObject to build a string.
* @author JSON.org
* @version 2016-08-08
*/
public class JSONWriter {
private static final int maxdepth = 200;
/**
* The comma flag determines if a comma should be output before the next
* value.
*/
private boolean comma;
/**
* The current mode. Values:
* 'a' (array),
* 'd' (done),
* 'i' (initial),
* 'k' (key),
* 'o' (object).
*/
protected char mode;
/**
* The object/array stack.
*/
private final JSONObject stack[];
/**
* The stack top index. A value of 0 indicates that the stack is empty.
*/
private int top;
/**
* The writer that will receive the output.
*/
protected Appendable writer;
/**
* Make a fresh JSONWriter. It can be used to build one JSON text.
*/
public JSONWriter(Appendable w) {
this.comma = false;
this.mode = 'i';
this.stack = new JSONObject[maxdepth];
this.top = 0;
this.writer = w;
}
/**
* Append a value.
* @param string A string value.
* @return this
* @throws JSONException If the value is out of sequence.
*/
private JSONWriter append(String string) throws JSONException {
if (string == null) {
throw new JSONException("Null pointer");
}
if (this.mode == 'o' || this.mode == 'a') {
try {
if (this.comma && this.mode == 'a') {
this.writer.append(',');
}
this.writer.append(string);
} catch (IOException e) {
// Android as of API 25 does not support this exception constructor
// however we won't worry about it. If an exception is happening here
// it will just throw a "Method not found" exception instead.
throw new JSONException(e);
}
if (this.mode == 'o') {
this.mode = 'k';
}
this.comma = true;
return this;
}
throw new JSONException("Value out of sequence.");
}
/**
* Begin appending a new array. All values until the balancing
* <code>endArray</code> will be appended to this array. The
* <code>endArray</code> method must be called to mark the array's end.
* @return this
* @throws JSONException If the nesting is too deep, or if the object is
* started in the wrong place (for example as a key or after the end of the
* outermost array or object).
*/
public JSONWriter array() throws JSONException {
if (this.mode == 'i' || this.mode == 'o' || this.mode == 'a') {
this.push(null);
this.append("[");
this.comma = false;
return this;
}
throw new JSONException("Misplaced array.");
}
/**
* End something.
* @param m Mode
* @param c Closing character
* @return this
* @throws JSONException If unbalanced.
*/
private JSONWriter end(char m, char c) throws JSONException {
if (this.mode != m) {
throw new JSONException(m == 'a'
? "Misplaced endArray."
: "Misplaced endObject.");
}
this.pop(m);
try {
this.writer.append(c);
} catch (IOException e) {
// Android as of API 25 does not support this exception constructor
// however we won't worry about it. If an exception is happening here
// it will just throw a "Method not found" exception instead.
throw new JSONException(e);
}
this.comma = true;
return this;
}
/**
* End an array. This method most be called to balance calls to
* <code>array</code>.
* @return this
* @throws JSONException If incorrectly nested.
*/
public JSONWriter endArray() throws JSONException {
return this.end('a', ']');
}
/**
* End an object. This method most be called to balance calls to
* <code>object</code>.
* @return this
* @throws JSONException If incorrectly nested.
*/
public JSONWriter endObject() throws JSONException {
return this.end('k', '}');
}
/**
* Append a key. The key will be associated with the next value. In an
* object, every value must be preceded by a key.
* @param string A key string.
* @return this
* @throws JSONException If the key is out of place. For example, keys
* do not belong in arrays or if the key is null.
*/
public JSONWriter key(String string) throws JSONException {
if (string == null) {
throw new JSONException("Null key.");
}
if (this.mode == 'k') {
try {
JSONObject topObject = this.stack[this.top - 1];
// don't use the built in putOnce method to maintain Android support
if(topObject.has(string)) {
throw new JSONException("Duplicate key \"" + string + "\"");
}
topObject.put(string, true);
if (this.comma) {
this.writer.append(',');
}
this.writer.append(JSONObject.quote(string));
this.writer.append(':');
this.comma = false;
this.mode = 'o';
return this;
} catch (IOException e) {
// Android as of API 25 does not support this exception constructor
// however we won't worry about it. If an exception is happening here
// it will just throw a "Method not found" exception instead.
throw new JSONException(e);
}
}
throw new JSONException("Misplaced key.");
}
/**
* Begin appending a new object. All keys and values until the balancing
* <code>endObject</code> will be appended to this object. The
* <code>endObject</code> method must be called to mark the object's end.
* @return this
* @throws JSONException If the nesting is too deep, or if the object is
* started in the wrong place (for example as a key or after the end of the
* outermost array or object).
*/
public JSONWriter object() throws JSONException {
if (this.mode == 'i') {
this.mode = 'o';
}
if (this.mode == 'o' || this.mode == 'a') {
this.append("{");
this.push(new JSONObject());
this.comma = false;
return this;
}
throw new JSONException("Misplaced object.");
}
/**
* Pop an array or object scope.
* @param c The scope to close.
* @throws JSONException If nesting is wrong.
*/
private void pop(char c) throws JSONException {
if (this.top <= 0) {
throw new JSONException("Nesting error.");
}
char m = this.stack[this.top - 1] == null ? 'a' : 'k';
if (m != c) {
throw new JSONException("Nesting error.");
}
this.top -= 1;
this.mode = this.top == 0
? 'd'
: this.stack[this.top - 1] == null
? 'a'
: 'k';
}
/**
* Push an array or object scope.
* @param jo The scope to open.
* @throws JSONException If nesting is too deep.
*/
private void push(JSONObject jo) throws JSONException {
if (this.top >= maxdepth) {
throw new JSONException("Nesting too deep.");
}
this.stack[this.top] = jo;
this.mode = jo == null ? 'a' : 'k';
this.top += 1;
}
/**
* Make a JSON text of an Object value. If the object has an
* value.toJSONString() method, then that method will be used to produce the
* JSON text. The method is required to produce a strictly conforming text.
* If the object does not contain a toJSONString method (which is the most
* common case), then a text will be produced by other means. If the value
* is an array or Collection, then a JSONArray will be made from it and its
* toJSONString method will be called. If the value is a MAP, then a
* JSONObject will be made from it and its toJSONString method will be
* called. Otherwise, the value's toString method will be called, and the
* result will be quoted.
*
* <p>
* Warning: This method assumes that the data structure is acyclical.
*
* @param value
* The value to be serialized.
* @return a printable, displayable, transmittable representation of the
* object, beginning with <code>{</code>&nbsp;<small>(left
* brace)</small> and ending with <code>}</code>&nbsp;<small>(right
* brace)</small>.
* @throws JSONException
* If the value is or contains an invalid number.
*/
public static String valueToString(Object value) throws JSONException {
if (value == null || value.equals(null)) {
return "null";
}
if (value instanceof JSONString) {
String object;
try {
object = ((JSONString) value).toJSONString();
} catch (Exception e) {
throw new JSONException(e);
}
if (object != null) {
return object;
}
throw new JSONException("Bad value from toJSONString: " + object);
}
if (value instanceof Number) {
// not all Numbers may match actual JSON Numbers. i.e. Fractions or Complex
final String numberAsString = JSONObject.numberToString((Number) value);
if(JSONObject.NUMBER_PATTERN.matcher(numberAsString).matches()) {
// Close enough to a JSON number that we will return it unquoted
return numberAsString;
}
// The Number value is not a valid JSON number.
// Instead we will quote it as a string
return JSONObject.quote(numberAsString);
}
if (value instanceof Boolean || value instanceof JSONObject
|| value instanceof JSONArray) {
return value.toString();
}
if (value instanceof Map) {
Map<?, ?> map = (Map<?, ?>) value;
return new JSONObject(map).toString();
}
if (value instanceof Collection) {
Collection<?> coll = (Collection<?>) value;
return new JSONArray(coll).toString();
}
if (value.getClass().isArray()) {
return new JSONArray(value).toString();
}
if(value instanceof Enum<?>){
return JSONObject.quote(((Enum<?>)value).name());
}
return JSONObject.quote(value.toString());
}
/**
* Append either the value <code>true</code> or the value
* <code>false</code>.
* @param b A boolean.
* @return this
* @throws JSONException
*/
public JSONWriter value(boolean b) throws JSONException {
return this.append(b ? "true" : "false");
}
/**
* Append a double value.
* @param d A double.
* @return this
* @throws JSONException If the number is not finite.
*/
public JSONWriter value(double d) throws JSONException {
return this.value(Double.valueOf(d));
}
/**
* Append a long value.
* @param l A long.
* @return this
* @throws JSONException
*/
public JSONWriter value(long l) throws JSONException {
return this.append(Long.toString(l));
}
/**
* Append an object value.
* @param object The object to append. It can be null, or a Boolean, Number,
* String, JSONObject, or JSONArray, or an object that implements JSONString.
* @return this
* @throws JSONException If the value is out of sequence.
*/
public JSONWriter value(Object object) throws JSONException {
return this.append(valueToString(object));
}
}

View File

@ -0,0 +1,24 @@
# 当前服务端玩法
GameMode: BlockWars
# 调试模式
MainDebug: false
# 请勿调动此参数
GiftDelay: 6000
# 直播平台
LivePlatform: DouYin
# 后台输出信息
Settings:
# 礼物
debug_gifts: false
# 点赞
debug_dianzan: false
# 关注
debug_guanzhu: false
# 进入
debug_join: true
# 聊天消息
debug_message: true
# 进入提示信息
join_show: true
AutoLink: false
LiveId: {}

View File

@ -0,0 +1,22 @@
# 1 = 左Shift 16 = 右Shift
# 2 = 左Ctrl 32 = 右Ctrl
# 8 = 左Alt 128 = 右Alt
32+21: 加油鸭 #右Ctrl+Y
32+22: 小心心 #右Ctrl+U
32+23: 玫瑰 #右Ctrl+I
32+24: 你最好看 #右Ctrl+O
32+25: 抖音 #右Ctrl+P
32+26: 爱你哟 #右Ctrl+{
32+27: 为你闪耀 #右Ctrl+}
32+43: 送你花花 #右Ctrl+|
32+34: 亲吻 #右Ctrl+G
32+35: 真的爱你 #右Ctrl+H
32+36: 闪耀星辰 #右Ctrl+J
32+37: 爱的纸鹤 #右Ctrl+K
32+38: 比心兔兔 #右Ctrl+L
32+39: 私人飞机 #右Ctrl+:
32+40: 直升机 #右Ctrl+"
32+47: 大啤酒 #右Ctrl+V
32+48: 人气票 #右Ctrl+B
32+49: 荧光棒 #右Ctrl+N
32+50: Thuglife #右Ctrl+M

View File

@ -0,0 +1,8 @@
name: McLiveAPI
version: 1.3
main: com.io.yutian.mclive.Main
api-version: 1.18
author: yutian
commands:
mclive:

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

24
target/classes/config.yml Normal file
View File

@ -0,0 +1,24 @@
# 当前服务端玩法
GameMode: BlockWars
# 调试模式
MainDebug: false
# 请勿调动此参数
GiftDelay: 6000
# 直播平台
LivePlatform: DouYin
# 后台输出信息
Settings:
# 礼物
debug_gifts: false
# 点赞
debug_dianzan: false
# 关注
debug_guanzhu: false
# 进入
debug_join: true
# 聊天消息
debug_message: true
# 进入提示信息
join_show: true
AutoLink: false
LiveId: {}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Some files were not shown because too many files have changed in this diff Show More