mirror of
https://github.com/ayunami2000/ayungee.git
synced 2024-12-21 14:24:10 -08:00
add first build
This commit is contained in:
commit
a744eb114a
2
.gitattributes
vendored
Normal file
2
.gitattributes
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
# Auto detect text files and perform LF normalization
|
||||||
|
* text=auto
|
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
.idea
|
||||||
|
web
|
||||||
|
target/*
|
||||||
|
!target/ayungee-1.0-SNAPSHOT-jar-with-dependencies.jar
|
29
LICENSE
Normal file
29
LICENSE
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
BSD 3-Clause License
|
||||||
|
|
||||||
|
Copyright (c) 2022, ayunami2000
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
1. Redistributions of source code must retain the above copyright notice, this
|
||||||
|
list of conditions and the following disclaimer.
|
||||||
|
|
||||||
|
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
this list of conditions and the following disclaimer in the documentation
|
||||||
|
and/or other materials provided with the distribution.
|
||||||
|
|
||||||
|
3. Neither the name of the copyright holder nor the names of its
|
||||||
|
contributors may be used to endorse or promote products derived from
|
||||||
|
this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||||
|
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
|
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||||
|
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
7
README.md
Normal file
7
README.md
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
# ayungee
|
||||||
|
|
||||||
|
lightweight bungeecord alternative for eaglercraft servers running protocolsupport
|
||||||
|
|
||||||
|
Thanks to LAX1DUDE for very small snippets of EaglerBungee used in this project (specifically for the server icon)
|
||||||
|
|
||||||
|
**TODO: skins & capes**
|
51
pom.xml
Normal file
51
pom.xml
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
<?xml version="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>me.ayunami2000</groupId>
|
||||||
|
<artifactId>ayungee</artifactId>
|
||||||
|
<version>1.0-SNAPSHOT</version>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<maven.compiler.source>8</maven.compiler.source>
|
||||||
|
<maven.compiler.target>8</maven.compiler.target>
|
||||||
|
</properties>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.java-websocket</groupId>
|
||||||
|
<artifactId>Java-WebSocket</artifactId>
|
||||||
|
<version>1.5.3</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.yaml</groupId>
|
||||||
|
<artifactId>snakeyaml</artifactId>
|
||||||
|
<version>1.30</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.googlecode.json-simple</groupId>
|
||||||
|
<artifactId>json-simple</artifactId>
|
||||||
|
<version>1.1.1</version>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
<build>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<artifactId>maven-assembly-plugin</artifactId>
|
||||||
|
<configuration>
|
||||||
|
<archive>
|
||||||
|
<manifest>
|
||||||
|
<mainClass>me.ayunami2000.ayungee.Main</mainClass>
|
||||||
|
</manifest>
|
||||||
|
</archive>
|
||||||
|
<descriptorRefs>
|
||||||
|
<descriptorRef>jar-with-dependencies</descriptorRef>
|
||||||
|
</descriptorRefs>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
</project>
|
28
src/main/java/me/ayunami2000/ayungee/Client.java
Normal file
28
src/main/java/me/ayunami2000/ayungee/Client.java
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
package me.ayunami2000.ayungee;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.net.Socket;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class Client {
|
||||||
|
public Socket socket = null;
|
||||||
|
public OutputStream socketOut = null;
|
||||||
|
public InputStream socketIn = null;
|
||||||
|
|
||||||
|
public List<byte[]> msgCache =new ArrayList<>();
|
||||||
|
|
||||||
|
public String username;
|
||||||
|
|
||||||
|
public void setSocket(Socket sock) throws IOException {
|
||||||
|
socket = sock;
|
||||||
|
socketOut = sock.getOutputStream();
|
||||||
|
socketIn = socket.getInputStream();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Client(String uname) {
|
||||||
|
username = uname;
|
||||||
|
}
|
||||||
|
}
|
279
src/main/java/me/ayunami2000/ayungee/Main.java
Normal file
279
src/main/java/me/ayunami2000/ayungee/Main.java
Normal file
|
@ -0,0 +1,279 @@
|
||||||
|
package me.ayunami2000.ayungee;
|
||||||
|
|
||||||
|
import org.java_websocket.WebSocket;
|
||||||
|
import org.java_websocket.server.WebSocketServer;
|
||||||
|
import org.json.simple.JSONObject;
|
||||||
|
import org.yaml.snakeyaml.Yaml;
|
||||||
|
|
||||||
|
import javax.imageio.ImageIO;
|
||||||
|
import java.io.*;
|
||||||
|
import java.net.HttpURLConnection;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.net.URLConnection;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
import java.nio.file.StandardCopyOption;
|
||||||
|
import java.time.Instant;
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
public class Main {
|
||||||
|
public static String hostname = "localhost";
|
||||||
|
public static int port = 25569;
|
||||||
|
public static int webPort = 25565;
|
||||||
|
|
||||||
|
public static String motdJson = "";
|
||||||
|
public static byte[] serverIcon = null;
|
||||||
|
|
||||||
|
public static boolean forwarded = false;
|
||||||
|
|
||||||
|
public static WebSocketServer webSocketServer = null;
|
||||||
|
|
||||||
|
public static Map<WebSocket, Client> clients = new HashMap<>();
|
||||||
|
|
||||||
|
public static Set<String> bans = new HashSet<>();
|
||||||
|
public static Set<Pattern> originBlacklist = new HashSet<>();
|
||||||
|
public static Set<String> originWhitelist = new HashSet<>();
|
||||||
|
|
||||||
|
public static void main(String[] args) throws IOException, InterruptedException {
|
||||||
|
Yaml yaml = new Yaml();
|
||||||
|
|
||||||
|
Map<String, Object> config;
|
||||||
|
|
||||||
|
try {
|
||||||
|
config = yaml.load(new FileReader("config.yml"));
|
||||||
|
} catch (FileNotFoundException e) {
|
||||||
|
Files.copy(Main.class.getResourceAsStream("/config.yml"), Paths.get("config.yml"), StandardCopyOption.REPLACE_EXISTING);
|
||||||
|
config = yaml.load(new FileReader("config.yml"));
|
||||||
|
}
|
||||||
|
|
||||||
|
File iconFile = new File("icon.png");
|
||||||
|
if (!iconFile.exists()) Files.copy(Main.class.getResourceAsStream("/icon.png"), Paths.get("icon.png"), StandardCopyOption.REPLACE_EXISTING);
|
||||||
|
int[] serverIconInt = ServerIcon.createServerIcon(ImageIO.read(iconFile));
|
||||||
|
byte[] iconPixels = new byte[16384];
|
||||||
|
for(int i = 0; i < 4096; ++i) {
|
||||||
|
iconPixels[i * 4] = (byte)((serverIconInt[i] >> 16) & 0xFF);
|
||||||
|
iconPixels[i * 4 + 1] = (byte)((serverIconInt[i] >> 8) & 0xFF);
|
||||||
|
iconPixels[i * 4 + 2] = (byte)(serverIconInt[i] & 0xFF);
|
||||||
|
iconPixels[i * 4 + 3] = (byte)((serverIconInt[i] >> 24) & 0xFF);
|
||||||
|
}
|
||||||
|
serverIcon = iconPixels;
|
||||||
|
|
||||||
|
|
||||||
|
try (BufferedReader br = new BufferedReader(new FileReader("bans.txt"))) {
|
||||||
|
String line;
|
||||||
|
while ((line = br.readLine()) != null) bans.add(line);
|
||||||
|
} catch (FileNotFoundException e) {
|
||||||
|
saveBans();
|
||||||
|
}
|
||||||
|
|
||||||
|
originWhitelist = new HashSet<>((List<String>) config.getOrDefault("origins", new ArrayList<>()));
|
||||||
|
|
||||||
|
String originBlacklistUrl = (String) config.getOrDefault("origin_blacklist", "https://g.eags.us/eaglercraft/origin_blacklist.txt");
|
||||||
|
if (originBlacklistUrl.isEmpty()) {
|
||||||
|
new Thread(() -> {
|
||||||
|
while (true) {
|
||||||
|
readUrlBlacklist();
|
||||||
|
try {
|
||||||
|
Thread.sleep(300000);
|
||||||
|
} catch (InterruptedException ignored) {}
|
||||||
|
}
|
||||||
|
}).start();
|
||||||
|
} else {
|
||||||
|
URL blacklistUrl = new URL(originBlacklistUrl);
|
||||||
|
new Thread(() -> {
|
||||||
|
while (true) {
|
||||||
|
try {
|
||||||
|
URLConnection cc = blacklistUrl.openConnection();
|
||||||
|
if (cc instanceof HttpURLConnection) {
|
||||||
|
HttpURLConnection ccc = (HttpURLConnection)cc;
|
||||||
|
ccc.setRequestProperty("Accept", "text/plain,text/html,application/xhtml+xml,application/xml");
|
||||||
|
ccc.setRequestProperty("User-Agent", "Mozilla/5.0 ayungee");
|
||||||
|
}
|
||||||
|
cc.connect();
|
||||||
|
Files.copy(cc.getInputStream(), Paths.get("origin_blacklist.txt"), StandardCopyOption.REPLACE_EXISTING);
|
||||||
|
readUrlBlacklist();
|
||||||
|
} catch (IOException e) {
|
||||||
|
System.out.println("An error occurred attempting to update the origin blacklist!");
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
Thread.sleep(300000);
|
||||||
|
} catch (InterruptedException ignored) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}).start();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
hostname = (String) config.getOrDefault("hostname", "localhost");
|
||||||
|
port = (int) config.getOrDefault("port", 25569);
|
||||||
|
webPort = (int) config.getOrDefault("web_port", 25565);
|
||||||
|
forwarded = (boolean) config.getOrDefault("forwarded", false);
|
||||||
|
|
||||||
|
List<String> defaultMotd = new ArrayList<>();
|
||||||
|
|
||||||
|
defaultMotd.add("Welcome to my");
|
||||||
|
defaultMotd.add("ayungee-powered server!");
|
||||||
|
|
||||||
|
List<String> defaultPlayersMotd = new ArrayList<>();
|
||||||
|
|
||||||
|
defaultPlayersMotd.add("whar?");
|
||||||
|
|
||||||
|
Map<String, Object> defaultConfigMotd = new HashMap<>();
|
||||||
|
|
||||||
|
defaultConfigMotd.put("lines", defaultMotd);
|
||||||
|
defaultConfigMotd.put("max", 20);
|
||||||
|
defaultConfigMotd.put("online", 4);
|
||||||
|
defaultConfigMotd.put("name", "An ayungee-powered Eaglercraft server");
|
||||||
|
defaultConfigMotd.put("players", defaultPlayersMotd);
|
||||||
|
|
||||||
|
Map<String, Object> configMotd = (LinkedHashMap<String, Object>) config.getOrDefault("motd", defaultConfigMotd);
|
||||||
|
|
||||||
|
JSONObject motdObj = new JSONObject();
|
||||||
|
JSONObject motdObjObj = new JSONObject();
|
||||||
|
|
||||||
|
motdObjObj.put("motd", configMotd.getOrDefault("lines", defaultMotd));
|
||||||
|
motdObjObj.put("cache", true);
|
||||||
|
motdObjObj.put("max", configMotd.get("max"));
|
||||||
|
motdObjObj.put("icon", true);
|
||||||
|
motdObjObj.put("online", configMotd.get("online"));
|
||||||
|
motdObjObj.put("players", configMotd.getOrDefault("players", defaultPlayersMotd));
|
||||||
|
|
||||||
|
motdObj.put("data", motdObjObj);
|
||||||
|
motdObj.put("vers", "0.2.0");
|
||||||
|
motdObj.put("name", configMotd.get("name"));
|
||||||
|
motdObj.put("time", Instant.now().toEpochMilli());
|
||||||
|
motdObj.put("type", "motd");
|
||||||
|
motdObj.put("brand", "Eagtek");
|
||||||
|
motdObj.put("uuid", UUID.randomUUID().toString());
|
||||||
|
motdObj.put("cracked", true);
|
||||||
|
|
||||||
|
motdJson = motdObj.toJSONString();
|
||||||
|
|
||||||
|
webSocketServer = new WebSocketProxy(webPort);
|
||||||
|
webSocketServer.start();
|
||||||
|
|
||||||
|
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
|
||||||
|
boolean running = true;
|
||||||
|
System.out.println("ayungee started!");
|
||||||
|
while (running) {
|
||||||
|
//System.out.print("> ");
|
||||||
|
String cmd = reader.readLine();
|
||||||
|
String[] pieces = cmd.split(" ");
|
||||||
|
pieces[0] = pieces[0].toLowerCase();
|
||||||
|
switch (pieces[0]) {
|
||||||
|
case "help":
|
||||||
|
case "?":
|
||||||
|
System.out.println("help ; unban <ip> ; banip <ip> ; ban <username> ; stop");
|
||||||
|
break;
|
||||||
|
case "unban":
|
||||||
|
case "pardon":
|
||||||
|
case "unban-ip":
|
||||||
|
case "unbanip":
|
||||||
|
case "pardon-ip":
|
||||||
|
case "pardonip":
|
||||||
|
if (pieces.length == 1) {
|
||||||
|
System.out.println("Usage: " + pieces[0] + " <ip>");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (bans.remove(pieces[1])) {
|
||||||
|
System.out.println("Successfully unbanned IP " + pieces[1]);
|
||||||
|
saveBans();
|
||||||
|
} else {
|
||||||
|
System.out.println("IP " + pieces[1] + " is not banned!");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "ban":
|
||||||
|
if (pieces.length == 1) {
|
||||||
|
System.out.println("Usage: " + pieces[0] + " <username>");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
//there should NEVER be duplicate usernames...
|
||||||
|
Client[] targetClients = clients.values().stream().filter(client -> client.username.equals(pieces[1])).toArray(Client[]::new);
|
||||||
|
if (targetClients.length == 0) targetClients = clients.values().stream().filter(client -> client.username.equalsIgnoreCase(pieces[1])).toArray(Client[]::new);
|
||||||
|
if (targetClients.length == 0) {
|
||||||
|
System.out.println("Unable to find any user with that username! (note: they must be online)");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
for (Client targetClient : targetClients) {
|
||||||
|
WebSocket targetWebSocket = getKeysByValue(clients, targetClient).stream().findFirst().orElse(null);
|
||||||
|
if (targetWebSocket == null) {
|
||||||
|
System.out.println("An internal error occurred which should never happen! Oops...");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
String ipToBan = getIp(targetWebSocket);
|
||||||
|
if (bans.add(ipToBan)) {
|
||||||
|
System.out.println("Successfully banned user " + targetClient.username + " with IP " + ipToBan);
|
||||||
|
try {
|
||||||
|
saveBans();
|
||||||
|
} catch (IOException ignored) {}
|
||||||
|
} else {
|
||||||
|
System.out.println("IP " + ipToBan + " is already banned!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "ban-ip":
|
||||||
|
case "banip":
|
||||||
|
if (pieces.length == 1) {
|
||||||
|
System.out.println("Usage: " + pieces[0] + " <ip>");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (bans.add(pieces[1])) {
|
||||||
|
System.out.println("Successfully banned IP " + pieces[1]);
|
||||||
|
saveBans();
|
||||||
|
} else {
|
||||||
|
System.out.println("IP " + pieces[1] + " is already banned!");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "stop":
|
||||||
|
case "end":
|
||||||
|
case "exit":
|
||||||
|
case "quit":
|
||||||
|
System.out.println("Stopping!");
|
||||||
|
running = false;
|
||||||
|
webSocketServer.stop(10);
|
||||||
|
System.exit(0);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
System.out.println("Command not found!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getIp(WebSocket conn) {
|
||||||
|
return conn.getAttachment();
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://stackoverflow.com/a/2904266/6917520
|
||||||
|
|
||||||
|
public static <T, E> Set<T> getKeysByValue(Map<T, E> map, E value) {
|
||||||
|
return map.entrySet()
|
||||||
|
.stream()
|
||||||
|
.filter(entry -> Objects.equals(entry.getValue(), value))
|
||||||
|
.map(Map.Entry::getKey)
|
||||||
|
.collect(Collectors.toSet());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void saveBans() throws IOException {
|
||||||
|
BufferedWriter writer = new BufferedWriter(new FileWriter("bans.txt"));
|
||||||
|
writer.write(String.join("\n", bans));
|
||||||
|
writer.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void readUrlBlacklist() {
|
||||||
|
try {
|
||||||
|
try (BufferedReader br = new BufferedReader(new FileReader("origin_blacklist.txt"))) {
|
||||||
|
originBlacklist.clear();
|
||||||
|
String line;
|
||||||
|
while ((line = br.readLine()) != null) {
|
||||||
|
if (line.startsWith("#")) continue;
|
||||||
|
if (line.trim().isEmpty()) continue;
|
||||||
|
originBlacklist.add(Pattern.compile(line, Pattern.CASE_INSENSITIVE));
|
||||||
|
}
|
||||||
|
} catch (FileNotFoundException e) {
|
||||||
|
new File("origin_blacklist.txt").createNewFile();
|
||||||
|
}
|
||||||
|
} catch (IOException ignored) {}
|
||||||
|
}
|
||||||
|
}
|
45
src/main/java/me/ayunami2000/ayungee/ServerIcon.java
Normal file
45
src/main/java/me/ayunami2000/ayungee/ServerIcon.java
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
package me.ayunami2000.ayungee;
|
||||||
|
|
||||||
|
import java.awt.*;
|
||||||
|
import java.awt.image.BufferedImage;
|
||||||
|
|
||||||
|
public class ServerIcon {
|
||||||
|
|
||||||
|
// https://github.com/LAX1DUDE/eaglercraft/blob/bec1a03fa24bcb9e8d07fd67ba82a6a827f9c1d9/eaglercraftbungee/src/main/java/net/md_5/bungee/api/ServerIcon.java
|
||||||
|
|
||||||
|
public static int[] createServerIcon(BufferedImage awtIcon) {
|
||||||
|
BufferedImage icon = awtIcon;
|
||||||
|
boolean gotScaled = false;
|
||||||
|
if(icon.getWidth() != 64 || icon.getHeight() != 64) {
|
||||||
|
icon = new BufferedImage(64, 64, awtIcon.getType());
|
||||||
|
Graphics2D g = (Graphics2D) icon.getGraphics();
|
||||||
|
g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, (awtIcon.getWidth() < 64 || awtIcon.getHeight() < 64) ?
|
||||||
|
RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR : RenderingHints.VALUE_INTERPOLATION_BICUBIC);
|
||||||
|
g.setBackground(Color.BLACK);
|
||||||
|
g.clearRect(0, 0, 64, 64);
|
||||||
|
int ow = awtIcon.getWidth();
|
||||||
|
int oh = awtIcon.getHeight();
|
||||||
|
int nw, nh;
|
||||||
|
float aspectRatio = (float)oh / (float)ow;
|
||||||
|
if(aspectRatio >= 1.0f) {
|
||||||
|
nw = (int)(64 / aspectRatio);
|
||||||
|
nh = 64;
|
||||||
|
}else {
|
||||||
|
nw = 64;
|
||||||
|
nh = (int)(64 * aspectRatio);
|
||||||
|
}
|
||||||
|
g.drawImage(awtIcon, (64 - nw) / 2, (64 - nh) / 2, (64 - nw) / 2 + nw, (64 - nh) / 2 + nh, 0, 0, awtIcon.getWidth(), awtIcon.getHeight(), null);
|
||||||
|
g.dispose();
|
||||||
|
gotScaled = true;
|
||||||
|
}
|
||||||
|
int[] pxls = icon.getRGB(0, 0, 64, 64, new int[4096], 0, 64);
|
||||||
|
if(gotScaled) {
|
||||||
|
for(int i = 0; i < pxls.length; ++i) {
|
||||||
|
if((pxls[i] & 0xFFFFFF) == 0) {
|
||||||
|
pxls[i] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return pxls;
|
||||||
|
}
|
||||||
|
}
|
161
src/main/java/me/ayunami2000/ayungee/WebSocketProxy.java
Normal file
161
src/main/java/me/ayunami2000/ayungee/WebSocketProxy.java
Normal file
|
@ -0,0 +1,161 @@
|
||||||
|
package me.ayunami2000.ayungee;
|
||||||
|
|
||||||
|
import org.java_websocket.WebSocket;
|
||||||
|
import org.java_websocket.handshake.ClientHandshake;
|
||||||
|
import org.java_websocket.server.WebSocketServer;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.InetSocketAddress;
|
||||||
|
import java.net.Socket;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.time.Instant;
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
public class WebSocketProxy extends WebSocketServer {
|
||||||
|
private static final int maxBuffSize = 33000; // 4096;
|
||||||
|
|
||||||
|
public WebSocketProxy(int port) {
|
||||||
|
super(new InetSocketAddress(port));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onOpen(WebSocket conn, ClientHandshake handshake) {
|
||||||
|
// anti-concurrentmodificationexception
|
||||||
|
Set<Pattern> snapshotOriginBlacklist = new HashSet<>(Main.originBlacklist);
|
||||||
|
if (!snapshotOriginBlacklist.isEmpty() || !Main.originWhitelist.isEmpty()) {
|
||||||
|
String origin = handshake.getFieldValue("Origin");
|
||||||
|
if (origin == null) {
|
||||||
|
conn.close();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!Main.originWhitelist.isEmpty() && !Main.originWhitelist.contains(origin)) {
|
||||||
|
conn.close();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (Pattern pattern : snapshotOriginBlacklist) {
|
||||||
|
if (pattern.matcher(origin).matches()) {
|
||||||
|
conn.close();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (Main.forwarded) {
|
||||||
|
String forwardedIp = handshake.getFieldValue("X-Real-IP");
|
||||||
|
if (forwardedIp == null) {
|
||||||
|
conn.close();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
conn.setAttachment(forwardedIp);
|
||||||
|
} else {
|
||||||
|
conn.setAttachment(conn.getRemoteSocketAddress().getAddress().toString().split("/")[1]);
|
||||||
|
}
|
||||||
|
new Thread(() -> {
|
||||||
|
try {
|
||||||
|
Thread.sleep(5000);
|
||||||
|
} catch (InterruptedException ignored) {}
|
||||||
|
if (conn.isOpen() && (Main.bans.contains(Main.getIp(conn)) || !Main.clients.containsKey(conn))) conn.close();
|
||||||
|
}).start();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onClose(WebSocket conn, int code, String reason, boolean remote) {
|
||||||
|
Client selfClient = Main.clients.remove(conn);
|
||||||
|
if (selfClient != null) {
|
||||||
|
System.out.println("Player " + selfClient.username + " (" + Main.getIp(conn) + ") left!");
|
||||||
|
if (selfClient.socket.isClosed()) {
|
||||||
|
try {
|
||||||
|
selfClient.socket.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
//e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onMessage(WebSocket conn, String s) {
|
||||||
|
if (!conn.isOpen()) return;
|
||||||
|
if (s.equals("Accept: MOTD")) {
|
||||||
|
if (Main.bans.contains(Main.getIp(conn))) {
|
||||||
|
conn.send("{\"data\":{\"motd\":[\"§cYour IP is §4banned §cfrom this server.\"],\"cache\":true,\"max\":0,\"icon\":false,\"online\":0,\"players\":[\"§4Banned...\"]},\"vers\":\"0.2.0\",\"name\":\"Your IP is banned from this server.\",\"time\":" + Instant.now().toEpochMilli() + ",\"type\":\"motd\",\"brand\":\"Eagtek\",\"uuid\":\"" + UUID.randomUUID().toString() + "\",\"cracked\":true}");
|
||||||
|
} else {
|
||||||
|
conn.send(Main.motdJson);
|
||||||
|
conn.send(Main.serverIcon);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
conn.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onMessage(WebSocket conn, ByteBuffer message) {
|
||||||
|
if (Main.bans.contains(Main.getIp(conn))) {
|
||||||
|
conn.close();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!conn.isOpen()) return;
|
||||||
|
byte[] msg = message.array();
|
||||||
|
if (!Main.clients.containsKey(conn)) {
|
||||||
|
if (msg.length > 3 && msg[1] == (byte) 69) {
|
||||||
|
if (msg[3] < 3 || msg[3] > 16) {
|
||||||
|
conn.close();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
byte[] uname = new byte[msg[3]];
|
||||||
|
if (msg.length < 5 + msg[3] * 2) {
|
||||||
|
conn.close();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < uname.length; i++) uname[i] = msg[5 + i * 2];
|
||||||
|
String username = new String(uname);
|
||||||
|
Main.clients.put(conn, new Client(username));
|
||||||
|
new Thread(() -> {
|
||||||
|
try {
|
||||||
|
Socket selfSocket = new Socket(Main.hostname, Main.port);
|
||||||
|
Client selfClient = Main.clients.get(conn);
|
||||||
|
selfClient.setSocket(selfSocket);
|
||||||
|
while (selfClient.msgCache.size() > 0) selfClient.socketOut.write(selfClient.msgCache.remove(0));
|
||||||
|
while (conn.isOpen() && !selfSocket.isInputShutdown()) {
|
||||||
|
byte[] data = new byte[maxBuffSize];
|
||||||
|
int read = selfClient.socketIn.read(data, 0, maxBuffSize);
|
||||||
|
if (read == maxBuffSize) {
|
||||||
|
if (conn.isOpen()) conn.send(data);
|
||||||
|
} else {
|
||||||
|
byte[] trueData = new byte[read];
|
||||||
|
System.arraycopy(data, 0, trueData, 0, read);
|
||||||
|
if (conn.isOpen()) conn.send(trueData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (conn.isOpen()) conn.close();
|
||||||
|
if (!selfSocket.isClosed()) selfSocket.close();
|
||||||
|
} catch (IOException ex) {
|
||||||
|
conn.close();
|
||||||
|
}
|
||||||
|
}).start();
|
||||||
|
msg[1] = (byte) 61;
|
||||||
|
System.out.println("Player " + username + " (" + Main.getIp(conn) + ") joined!");
|
||||||
|
} else {
|
||||||
|
conn.close();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Client currClient = Main.clients.get(conn);
|
||||||
|
if (currClient.socketOut == null) {
|
||||||
|
currClient.msgCache.add(message.array());
|
||||||
|
} else if (!currClient.socket.isOutputShutdown()) {
|
||||||
|
try {
|
||||||
|
currClient.socketOut.write(message.array());
|
||||||
|
} catch (IOException ignored) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onError(WebSocket conn, Exception ex) {
|
||||||
|
//
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onStart() {
|
||||||
|
setConnectionLostTimeout(0);
|
||||||
|
}
|
||||||
|
}
|
29
src/main/resources/config.yml
Normal file
29
src/main/resources/config.yml
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
# mc server ip
|
||||||
|
hostname: localhost
|
||||||
|
# mc server port
|
||||||
|
port: 25569
|
||||||
|
# ayungee port
|
||||||
|
web_port: 25565
|
||||||
|
# if this is behind a reverse proxy, such as caddy or nginx. uses X-Real-IP header.
|
||||||
|
forwarded: false
|
||||||
|
# origin blacklist URL (leave empty to disable syncing)
|
||||||
|
origin_blacklist: "https://g.eags.us/eaglercraft/origin_blacklist.txt"
|
||||||
|
# whitelisted origins -- if specified, only allows the listed origins to connect
|
||||||
|
# for example,
|
||||||
|
# - "https://g.eags.us"
|
||||||
|
origins: []
|
||||||
|
# motd info
|
||||||
|
motd:
|
||||||
|
# the motd itself
|
||||||
|
lines:
|
||||||
|
- "Welcome to my"
|
||||||
|
- "ayungee-powered server!"
|
||||||
|
# max players (purely visual)
|
||||||
|
max: 20
|
||||||
|
# online players (purely visual)
|
||||||
|
online: 4
|
||||||
|
# name of server
|
||||||
|
name: An ayungee-powered Eaglercraft server
|
||||||
|
# players online (purely visual)
|
||||||
|
players:
|
||||||
|
- "whar?"
|
BIN
src/main/resources/icon.png
Normal file
BIN
src/main/resources/icon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 450 B |
BIN
target/ayungee-1.0-SNAPSHOT-jar-with-dependencies.jar
Normal file
BIN
target/ayungee-1.0-SNAPSHOT-jar-with-dependencies.jar
Normal file
Binary file not shown.
Loading…
Reference in New Issue
Block a user