Skip to content
This repository has been archived by the owner on Oct 20, 2023. It is now read-only.

Commit

Permalink
1.3.8-RELEASE!
Browse files Browse the repository at this point in the history
- 新增查询MC服务器信息
- 新增 Rcon 功能
- 新增机器人管理员功能
- 新增群自动欢迎 & 自动接受入群请求
- 新增被主人邀请入群自动接受
- 修复了一些 Bug
- 新增了更多 Bug
  • Loading branch information
StarWishsama committed Apr 27, 2019
1 parent 9bc1513 commit dbae137
Show file tree
Hide file tree
Showing 19 changed files with 1,325 additions and 97 deletions.
1 change: 1 addition & 0 deletions Version.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
1.3.8-RELEASE
4 changes: 3 additions & 1 deletion build.gradle
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
group 'top.starwish'
version '1.3.4-RELEASE'
version '1.3.8-RELEASE'

apply plugin: 'java'

Expand All @@ -24,10 +24,12 @@ dependencies {
compile 'org.apache.httpcomponents:httpclient:4.5.2'
compile 'org.apache.httpcomponents:httpcore:4.4.4'
compile 'org.apache.commons:commons-lang3:3.8.1'
compile 'org.apache.commons:commons-io:1.3.2'
compile 'org.jdom:jdom2:2.0.6'
compile 'org.slf4j:slf4j-api:1.7.16'
compile 'org.slf4j:slf4j-nop:1.7.9'
compile 'com.alibaba:fastjson:1.2.56'
testCompile 'junit:junit:4.12'
}

//preBuild.dependsOn copyJars
Expand Down
128 changes: 128 additions & 0 deletions src/main/java/net/kronos/rkon/core/Rcon.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
package net.kronos.rkon.core;

import java.io.IOException;
import java.net.Socket;
import java.nio.charset.Charset;
import java.util.Random;

import net.kronos.rkon.core.ex.AuthenticationException;

import top.starwish.namelessbot.utils.Utils;

public class Rcon {

private final Object sync = new Object();
private final Random rand = new Random();

private int requestId;
private Socket socket;

private Charset charset;

/**
* Create, connect and authenticate a new Rcon object
*
* @param host Rcon server address
* @param port Rcon server port
* @param password Rcon server password
*
* @throws IOException
* @throws AuthenticationException
*/
public Rcon(String host, int port, byte[] password) throws IOException, AuthenticationException {
// Default charset is utf8
this.charset = Charset.forName("UTF-8");

// Connect to host
this.connect(host, port, password);
}

/**
* Connect to a rcon server
*
* @param host Rcon server address
* @param port Rcon server port
* @param password Rcon server password
*
* @throws IOException
* @throws AuthenticationException
*/
public void connect(String host, int port, byte[] password) throws IOException, AuthenticationException {
if(host == null || host.trim().isEmpty()) {
throw new IllegalArgumentException("Host can't be null or empty");
}

if(port < 1 || port > 65535) {
throw new IllegalArgumentException("Port is out of range");
}

// Connect to the rcon server
synchronized(sync) {
// New random request id
this.requestId = rand.nextInt();

// We can't reuse a socket, so we need a new one
this.socket = new Socket(host, port);
}

// Send the auth packet
RconPacket res = this.send(RconPacket.SERVERDATA_AUTH, password);

// Auth failed
if(res.getRequestId() == -1) {
throw new AuthenticationException("Password rejected by server");
}
}

/**
* Disconnect from the current server
*
* @throws IOException
*/
public void disconnect() throws IOException {
synchronized(sync) {
this.socket.close();
}
}

/**
* Send a command to the server
*
* @param payload The command to send
* @return The payload of the response
*
* @throws IOException
*/
public String command(String payload) throws IOException {
if (payload == null || payload.trim().isEmpty()) {
throw new IllegalArgumentException("Payload can't be null or empty");
}

RconPacket response = this.send(RconPacket.SERVERDATA_EXECCOMMAND, payload.getBytes());

return Utils.deColor(new String(response.getPayload(), this.getCharset()));
}

private RconPacket send(int type, byte[] payload) throws IOException {
synchronized(sync) {
return RconPacket.send(this, type, payload);
}
}

public int getRequestId() {
return requestId;
}

public Socket getSocket() {
return socket;
}

public Charset getCharset() {
return charset;
}

public void setCharset(Charset charset) {
this.charset = charset;
}

}
152 changes: 152 additions & 0 deletions src/main/java/net/kronos/rkon/core/RconPacket.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
package net.kronos.rkon.core;

import java.io.DataInputStream;
import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.SocketException;
import java.nio.BufferUnderflowException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;

import net.kronos.rkon.core.ex.MalformedPacketException;

public class RconPacket {

public static final int SERVERDATA_EXECCOMMAND = 2;
public static final int SERVERDATA_AUTH = 3;

private int requestId;
private int type;
private byte[] payload;

private RconPacket(int requestId, int type, byte[] payload) {
this.requestId = requestId;
this.type = type;
this.payload = payload;
}

public int getRequestId() {
return requestId;
}

public int getType() {
return type;
}

public byte[] getPayload() {
return payload;
}

/**
* Send a Rcon packet and fetch the response
*
* @param rcon Rcon instance
* @param type The packet type
* @param payload The payload (password, command, etc.)
* @return A RconPacket object containing the response
*
* @throws IOException
* @throws MalformedPacketException
*/
protected static RconPacket send(Rcon rcon, int type, byte[] payload) throws IOException {
try {
RconPacket.write(rcon.getSocket().getOutputStream(), rcon.getRequestId(), type, payload);
}
catch(SocketException se) {
// Close the socket if something happens
rcon.getSocket().close();

// Rethrow the exception
throw se;
}

return RconPacket.read(rcon.getSocket().getInputStream());
}

/**
* Write a rcon packet on an outputstream
*
* @param out The OutputStream to write on
* @param requestId The request id
* @param type The packet type
* @param payload The payload
*
* @throws IOException
*/
private static void write(OutputStream out, int requestId, int type, byte[] payload) throws IOException {
int bodyLength = RconPacket.getBodyLength(payload.length);
int packetLength = RconPacket.getPacketLength(bodyLength);

ByteBuffer buffer = ByteBuffer.allocate(packetLength);
buffer.order(ByteOrder.LITTLE_ENDIAN);

buffer.putInt(bodyLength);
buffer.putInt(requestId);
buffer.putInt(type);
buffer.put(payload);

// Null bytes terminators
buffer.put((byte)0);
buffer.put((byte)0);

// Woosh!
out.write(buffer.array());
out.flush();
}

/**
* Read an incoming rcon packet
*
* @param in The InputStream to read on
* @return The read RconPacket
*
* @throws IOException
* @throws MalformedPacketException
*/
private static RconPacket read(InputStream in) throws IOException {
// Header is 3 4-bytes ints
byte[] header = new byte[4 * 3];

// Read the 3 ints
in.read(header);

try {
// Use a bytebuffer in little endian to read the first 3 ints
ByteBuffer buffer = ByteBuffer.wrap(header);
buffer.order(ByteOrder.LITTLE_ENDIAN);

int length = buffer.getInt();
int requestId = buffer.getInt();
int type = buffer.getInt();

// Payload size can be computed now that we have its length
byte[] payload = new byte[length - 4 - 4 - 2];

DataInputStream dis = new DataInputStream(in);

// Read the full payload
dis.readFully(payload);

// Read the null bytes
dis.read(new byte[2]);

return new RconPacket(requestId, type, payload);
}
catch(BufferUnderflowException | EOFException e) {
throw new MalformedPacketException("Cannot read the whole packet");
}
}

private static int getPacketLength(int bodyLength) {
// 4 bytes for length + x bytes for body length
return 4 + bodyLength;
}

private static int getBodyLength(int payloadLength) {
// 4 bytes for requestId, 4 bytes for type, x bytes for payload, 2 bytes for two null bytes
return 4 + 4 + payloadLength + 2;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package net.kronos.rkon.core.ex;

public class AuthenticationException extends Exception {

public AuthenticationException(String message) {
super(message);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package net.kronos.rkon.core.ex;

import java.io.IOException;

public class MalformedPacketException extends IOException {

public MalformedPacketException(String message) {
super(message);
}

}
Loading

0 comments on commit dbae137

Please sign in to comment.