mirror of
https://github.com/LOOHP/Limbo.git
synced 2026-06-07 21:41:43 +00:00
Reorganize sources to maven project standard
This commit is contained in:
@@ -0,0 +1,7 @@
|
||||
package com.loohp.limbo.Commands;
|
||||
|
||||
public interface CommandExecutor {
|
||||
|
||||
public void execute(CommandSender sender, String[] args);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package com.loohp.limbo.Commands;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
import net.md_5.bungee.api.chat.BaseComponent;
|
||||
|
||||
public interface CommandSender {
|
||||
|
||||
public void sendMessage(BaseComponent[] component, UUID uuid);
|
||||
|
||||
public void sendMessage(BaseComponent component, UUID uuid);
|
||||
|
||||
public void sendMessage(String message, UUID uuid);
|
||||
|
||||
public void sendMessage(BaseComponent[] component);
|
||||
|
||||
public void sendMessage(BaseComponent component);
|
||||
|
||||
public void sendMessage(String message);
|
||||
|
||||
public boolean hasPermission(String permission);
|
||||
|
||||
public String getName();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package com.loohp.limbo.Commands;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface TabCompletor {
|
||||
|
||||
public List<String> tabComplete(CommandSender sender, String[] args);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,482 @@
|
||||
package com.loohp.limbo;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.PrintStream;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Arrays;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.fusesource.jansi.Ansi;
|
||||
import org.fusesource.jansi.Ansi.Attribute;
|
||||
import org.jline.reader.Candidate;
|
||||
import org.jline.reader.Completer;
|
||||
import org.jline.reader.LineReader;
|
||||
import org.jline.reader.LineReader.SuggestionType;
|
||||
import org.jline.reader.LineReaderBuilder;
|
||||
import org.jline.reader.ParsedLine;
|
||||
import org.jline.terminal.Terminal;
|
||||
import org.jline.terminal.TerminalBuilder;
|
||||
|
||||
import com.loohp.limbo.Commands.CommandSender;
|
||||
import com.loohp.limbo.GUI.ConsoleTextOutput;
|
||||
import com.loohp.limbo.Utils.CustomStringUtils;
|
||||
|
||||
import jline.console.ConsoleReader;
|
||||
import net.md_5.bungee.api.ChatColor;
|
||||
import net.md_5.bungee.api.chat.BaseComponent;
|
||||
|
||||
public class Console implements CommandSender {
|
||||
|
||||
protected static final Map<ChatColor, String> REPLACEMENTS = new HashMap<>();
|
||||
private final static String CONSOLE = "CONSOLE";
|
||||
private final static String PROMPT = "> ";
|
||||
protected final static String ERROR_RED = "\u001B[31;1m";
|
||||
protected final static String RESET_COLOR = "\u001B[0m";
|
||||
|
||||
private Terminal terminal;
|
||||
private LineReader tabReader;
|
||||
private ConsoleReader reader;
|
||||
|
||||
private InputStream in;
|
||||
@SuppressWarnings("unused")
|
||||
private PrintStream out;
|
||||
@SuppressWarnings("unused")
|
||||
private PrintStream err;
|
||||
protected PrintStream logs;
|
||||
|
||||
public Console(InputStream in, PrintStream out, PrintStream err) throws IOException {
|
||||
String fileName = new SimpleDateFormat("yyyy'-'MM'-'dd'_'HH'-'mm'-'ss'_'zzz'.log'").format(new Date());
|
||||
File dir = new File("logs");
|
||||
dir.mkdirs();
|
||||
File logs = new File(dir, fileName);
|
||||
this.logs = new PrintStream(logs);
|
||||
|
||||
if (in != null) {
|
||||
System.setIn(in);
|
||||
this.in = System.in;
|
||||
} else {
|
||||
this.in = null;
|
||||
}
|
||||
System.setOut(new ConsoleOutputStream(this, out == null ? new PrintStream(new OutputStream() {
|
||||
@Override
|
||||
public void write(int b) {
|
||||
//DO NOTHING
|
||||
}
|
||||
}) : out, this.logs));
|
||||
this.out = System.out;
|
||||
|
||||
System.setErr(new ConsoleErrorStream(this, err == null ? new PrintStream(new OutputStream() {
|
||||
@Override
|
||||
public void write(int b) {
|
||||
//DO NOTHING
|
||||
}
|
||||
}) : err, this.logs));
|
||||
this.err = System.err;
|
||||
|
||||
reader = new ConsoleReader(in, out);
|
||||
reader.setExpandEvents(false);
|
||||
|
||||
|
||||
terminal = TerminalBuilder.builder().streams(in, out).system(true).jansi(true).build();
|
||||
tabReader = LineReaderBuilder.builder().terminal(terminal).completer(new Completer() {
|
||||
@Override
|
||||
public void complete(LineReader reader, ParsedLine line, List<Candidate> candidates) {
|
||||
String[] args = CustomStringUtils.splitStringToArgs(line.line());
|
||||
List<String> tab = Limbo.getInstance().getPluginManager().getTabOptions(Limbo.getInstance().getConsole(), args);
|
||||
for (String each : tab) {
|
||||
candidates.add(new Candidate(each));
|
||||
}
|
||||
}
|
||||
}).build();
|
||||
tabReader.setAutosuggestion(SuggestionType.NONE);
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return CONSOLE;
|
||||
}
|
||||
|
||||
public boolean hasPermission(String permission) {
|
||||
return Limbo.getInstance().getPermissionsManager().hasPermission(this, permission);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendMessage(BaseComponent component, UUID uuid) {
|
||||
sendMessage(component);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendMessage(BaseComponent[] component, UUID uuid) {
|
||||
sendMessage(component);
|
||||
}
|
||||
|
||||
public void sendMessage(String message, UUID uuid) {
|
||||
sendMessage(message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendMessage(BaseComponent component) {
|
||||
sendMessage(new BaseComponent[] {component});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendMessage(BaseComponent[] component) {
|
||||
sendMessage(String.join("", Arrays.asList(component).stream().map(each -> each.toLegacyText()).collect(Collectors.toList())));
|
||||
}
|
||||
|
||||
public void sendMessage(String message) {
|
||||
stashLine();
|
||||
String date = new SimpleDateFormat("HH':'mm':'ss").format(new Date());
|
||||
ConsoleTextOutput.appendText(ChatColor.stripColor("[" + date + " Info] " + message), true);
|
||||
logs.println(ChatColor.stripColor("[" + date + " Info] " + message));
|
||||
try {
|
||||
reader.getOutput().append("[" + date + " Info] " + translateToConsole(message) + "\n");
|
||||
reader.getOutput().flush();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
unstashLine();
|
||||
}
|
||||
|
||||
protected void run() {
|
||||
if (in == null) {
|
||||
return;
|
||||
}
|
||||
while (true) {
|
||||
String command = tabReader.readLine(PROMPT);
|
||||
if (command.length() > 0) {
|
||||
String[] input = CustomStringUtils.splitStringToArgs(command);
|
||||
new Thread(() -> Limbo.getInstance().dispatchCommand(this, input)).start();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void stashLine() {
|
||||
try {
|
||||
tabReader.callWidget(LineReader.CLEAR);
|
||||
} catch (Exception ignore) {}
|
||||
}
|
||||
|
||||
protected void unstashLine() {
|
||||
try {
|
||||
tabReader.callWidget(LineReader.REDRAW_LINE);
|
||||
tabReader.callWidget(LineReader.REDISPLAY);
|
||||
tabReader.getTerminal().writer().flush();
|
||||
} catch (Exception ignore) {}
|
||||
}
|
||||
|
||||
static {
|
||||
REPLACEMENTS.put(ChatColor.BLACK, Ansi.ansi().a(Attribute.RESET).fg(Ansi.Color.BLACK).boldOff().toString());
|
||||
REPLACEMENTS.put(ChatColor.DARK_BLUE, Ansi.ansi().a(Attribute.RESET).fg(Ansi.Color.BLUE).boldOff().toString());
|
||||
REPLACEMENTS.put(ChatColor.DARK_GREEN, Ansi.ansi().a(Attribute.RESET).fg(Ansi.Color.GREEN).boldOff().toString());
|
||||
REPLACEMENTS.put(ChatColor.DARK_AQUA, Ansi.ansi().a(Attribute.RESET).fg(Ansi.Color.CYAN).boldOff().toString());
|
||||
REPLACEMENTS.put(ChatColor.DARK_RED, Ansi.ansi().a(Attribute.RESET).fg(Ansi.Color.RED).boldOff().toString());
|
||||
REPLACEMENTS.put(ChatColor.DARK_PURPLE, Ansi.ansi().a(Attribute.RESET).fg(Ansi.Color.MAGENTA).boldOff().toString());
|
||||
REPLACEMENTS.put(ChatColor.GOLD, Ansi.ansi().a(Attribute.RESET).fg(Ansi.Color.YELLOW).boldOff().toString());
|
||||
REPLACEMENTS.put(ChatColor.GRAY, Ansi.ansi().a(Attribute.RESET).fg(Ansi.Color.WHITE).boldOff().toString());
|
||||
REPLACEMENTS.put(ChatColor.DARK_GRAY, Ansi.ansi().a(Attribute.RESET).fg(Ansi.Color.BLACK).bold().toString());
|
||||
REPLACEMENTS.put(ChatColor.BLUE, Ansi.ansi().a(Attribute.RESET).fg(Ansi.Color.BLUE).bold().toString());
|
||||
REPLACEMENTS.put(ChatColor.GREEN, Ansi.ansi().a(Attribute.RESET).fg(Ansi.Color.GREEN).bold().toString());
|
||||
REPLACEMENTS.put(ChatColor.AQUA, Ansi.ansi().a(Attribute.RESET).fg(Ansi.Color.CYAN).bold().toString());
|
||||
REPLACEMENTS.put(ChatColor.RED, Ansi.ansi().a(Attribute.RESET).fg(Ansi.Color.RED).bold().toString());
|
||||
REPLACEMENTS.put(ChatColor.LIGHT_PURPLE, Ansi.ansi().a(Attribute.RESET).fg(Ansi.Color.MAGENTA).bold().toString());
|
||||
REPLACEMENTS.put(ChatColor.YELLOW, Ansi.ansi().a(Attribute.RESET).fg(Ansi.Color.YELLOW).bold().toString());
|
||||
REPLACEMENTS.put(ChatColor.WHITE, Ansi.ansi().a(Attribute.RESET).fg(Ansi.Color.WHITE).bold().toString());
|
||||
REPLACEMENTS.put(ChatColor.MAGIC, Ansi.ansi().a(Attribute.BLINK_SLOW).toString());
|
||||
REPLACEMENTS.put(ChatColor.BOLD, Ansi.ansi().a(Attribute.UNDERLINE_DOUBLE).toString());
|
||||
REPLACEMENTS.put(ChatColor.STRIKETHROUGH, Ansi.ansi().a(Attribute.STRIKETHROUGH_ON).toString());
|
||||
REPLACEMENTS.put(ChatColor.UNDERLINE, Ansi.ansi().a(Attribute.UNDERLINE).toString());
|
||||
REPLACEMENTS.put(ChatColor.ITALIC, Ansi.ansi().a(Attribute.ITALIC).toString());
|
||||
REPLACEMENTS.put(ChatColor.RESET, Ansi.ansi().a(Attribute.RESET).toString());
|
||||
}
|
||||
|
||||
protected static String translateToConsole(String str) {
|
||||
for (Entry<ChatColor, String> entry : REPLACEMENTS.entrySet()) {
|
||||
str = str.replace(entry.getKey().toString(), entry.getValue());
|
||||
}
|
||||
str = str.replaceAll("(?i)" + ChatColor.COLOR_CHAR + "x(" + ChatColor.COLOR_CHAR + "[0-9a-f]){6}", "");
|
||||
return str + RESET_COLOR;
|
||||
}
|
||||
|
||||
public static class ConsoleOutputStream extends PrintStream {
|
||||
|
||||
private PrintStream logs;
|
||||
private Console console;
|
||||
|
||||
public ConsoleOutputStream(Console console, OutputStream out, PrintStream logs) {
|
||||
super(out);
|
||||
this.logs = logs;
|
||||
this.console = console;
|
||||
}
|
||||
|
||||
@SuppressWarnings("resource")
|
||||
@Override
|
||||
public PrintStream printf(Locale l, String format, Object... args) {
|
||||
console.stashLine();
|
||||
String date = new SimpleDateFormat("HH':'mm':'ss").format(new Date());
|
||||
ConsoleTextOutput.appendText(ChatColor.stripColor(String.format(l, "[" + date + " Info]" + format, args)));
|
||||
logs.printf(l, ChatColor.stripColor("[" + date + " Info]" + format), args);
|
||||
PrintStream stream = super.printf(l, Console.translateToConsole("[" + date + " Info]" + format), args);
|
||||
console.unstashLine();
|
||||
return stream;
|
||||
}
|
||||
|
||||
@SuppressWarnings("resource")
|
||||
@Override
|
||||
public PrintStream printf(String format, Object... args) {
|
||||
console.stashLine();
|
||||
String date = new SimpleDateFormat("HH':'mm':'ss").format(new Date());
|
||||
ConsoleTextOutput.appendText(ChatColor.stripColor(String.format("[" + date + " Info]" + format, args)));
|
||||
logs.printf(ChatColor.stripColor("[" + date + " Info]" + format), args);
|
||||
PrintStream stream = super.printf(ChatColor.stripColor("[" + date + " Info]" + format), args);
|
||||
console.unstashLine();
|
||||
return stream;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void println() {
|
||||
console.stashLine();
|
||||
String date = new SimpleDateFormat("HH':'mm':'ss").format(new Date());
|
||||
ConsoleTextOutput.appendText(ChatColor.stripColor("[" + date + " Info]"), true);
|
||||
logs.println(ChatColor.stripColor("[" + date + " Info]"));
|
||||
super.println(ChatColor.stripColor("[" + date + " Info]"));
|
||||
console.unstashLine();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void println(boolean x) {
|
||||
console.stashLine();
|
||||
String date = new SimpleDateFormat("HH':'mm':'ss").format(new Date());
|
||||
ConsoleTextOutput.appendText(ChatColor.stripColor("[" + date + " Info] " + x), true);
|
||||
logs.println(ChatColor.stripColor("[" + date + " Info]" + x));
|
||||
super.println(ChatColor.stripColor("[" + date + " Info]" + x));
|
||||
console.unstashLine();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void println(char x) {
|
||||
console.stashLine();
|
||||
String date = new SimpleDateFormat("HH':'mm':'ss").format(new Date());
|
||||
ConsoleTextOutput.appendText(ChatColor.stripColor("[" + date + " Info] " + x), true);
|
||||
logs.println(ChatColor.stripColor("[" + date + " Info]" + x));
|
||||
super.println(ChatColor.stripColor("[" + date + " Info]" + x));
|
||||
console.unstashLine();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void println(char[] x) {
|
||||
console.stashLine();
|
||||
String date = new SimpleDateFormat("HH':'mm':'ss").format(new Date());
|
||||
ConsoleTextOutput.appendText(ChatColor.stripColor("[" + date + " Info] " + String.valueOf(x)), true);
|
||||
logs.println(ChatColor.stripColor("[" + date + " Info]" + String.valueOf(x)));
|
||||
super.println(ChatColor.stripColor("[" + date + " Info]" + String.valueOf(x)));
|
||||
console.unstashLine();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void println(double x) {
|
||||
console.stashLine();
|
||||
String date = new SimpleDateFormat("HH':'mm':'ss").format(new Date());
|
||||
ConsoleTextOutput.appendText(ChatColor.stripColor("[" + date + " Info] " + x), true);
|
||||
logs.println(ChatColor.stripColor("[" + date + " Info]" + x));
|
||||
super.println(ChatColor.stripColor("[" + date + " Info]" + x));
|
||||
console.unstashLine();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void println(float x) {
|
||||
console.stashLine();
|
||||
String date = new SimpleDateFormat("HH':'mm':'ss").format(new Date());
|
||||
ConsoleTextOutput.appendText(ChatColor.stripColor("[" + date + " Info] " + x), true);
|
||||
logs.println(ChatColor.stripColor("[" + date + " Info]" + x));
|
||||
super.println(ChatColor.stripColor("[" + date + " Info]" + x));
|
||||
console.unstashLine();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void println(int x) {
|
||||
console.stashLine();
|
||||
String date = new SimpleDateFormat("HH':'mm':'ss").format(new Date());
|
||||
ConsoleTextOutput.appendText(ChatColor.stripColor("[" + date + " Info] " + x), true);
|
||||
logs.println(ChatColor.stripColor("[" + date + " Info]" + x));
|
||||
super.println(ChatColor.stripColor("[" + date + " Info]" + x));
|
||||
console.unstashLine();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void println(long x) {
|
||||
console.stashLine();
|
||||
String date = new SimpleDateFormat("HH':'mm':'ss").format(new Date());
|
||||
ConsoleTextOutput.appendText(ChatColor.stripColor("[" + date + " Info] " + x), true);
|
||||
logs.println(ChatColor.stripColor("[" + date + " Info]" + x));
|
||||
super.println(ChatColor.stripColor("[" + date + " Info]" + x));
|
||||
console.unstashLine();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void println(Object x) {
|
||||
console.stashLine();
|
||||
String date = new SimpleDateFormat("HH':'mm':'ss").format(new Date());
|
||||
ConsoleTextOutput.appendText(ChatColor.stripColor("[" + date + " Info] " + x), true);
|
||||
logs.println(ChatColor.stripColor("[" + date + " Info]" + x));
|
||||
super.println(ChatColor.stripColor("[" + date + " Info]" + x));
|
||||
console.unstashLine();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void println(String string) {
|
||||
console.stashLine();
|
||||
String date = new SimpleDateFormat("HH':'mm':'ss").format(new Date());
|
||||
ConsoleTextOutput.appendText(ChatColor.stripColor("[" + date + " Info] " + string), true);
|
||||
logs.println(ChatColor.stripColor("[" + date + " Info] " + string));
|
||||
super.println(ChatColor.stripColor("[" + date + " Info] " + string));
|
||||
console.unstashLine();
|
||||
}
|
||||
}
|
||||
|
||||
public static class ConsoleErrorStream extends PrintStream {
|
||||
|
||||
private PrintStream logs;
|
||||
private Console console;
|
||||
|
||||
public ConsoleErrorStream(Console console, OutputStream out, PrintStream logs) {
|
||||
super(out);
|
||||
this.logs = logs;
|
||||
this.console = console;
|
||||
}
|
||||
|
||||
@SuppressWarnings("resource")
|
||||
@Override
|
||||
public PrintStream printf(Locale l, String format, Object... args) {
|
||||
console.stashLine();
|
||||
String date = new SimpleDateFormat("HH':'mm':'ss").format(new Date());
|
||||
ConsoleTextOutput.appendText(ChatColor.stripColor(String.format(l, "[" + date + " Error]" + format, args)));
|
||||
logs.printf(l, ChatColor.stripColor("[" + date + " Error]" + format), args);
|
||||
PrintStream stream = super.printf(l, ERROR_RED + ChatColor.stripColor("[" + date + " Error]" + format + RESET_COLOR), args);
|
||||
console.unstashLine();
|
||||
return stream;
|
||||
}
|
||||
|
||||
@SuppressWarnings("resource")
|
||||
@Override
|
||||
public PrintStream printf(String format, Object... args) {
|
||||
console.stashLine();
|
||||
String date = new SimpleDateFormat("HH':'mm':'ss").format(new Date());
|
||||
ConsoleTextOutput.appendText(ChatColor.stripColor(String.format("[" + date + " Error]" + format, args)));
|
||||
logs.printf(ChatColor.stripColor("[" + date + " Error]" + format), args);
|
||||
PrintStream stream = super.printf(ERROR_RED + ChatColor.stripColor("[" + date + " Error]" + format + RESET_COLOR), args);
|
||||
console.unstashLine();
|
||||
return stream;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void println() {
|
||||
console.stashLine();
|
||||
String date = new SimpleDateFormat("HH':'mm':'ss").format(new Date());
|
||||
ConsoleTextOutput.appendText(ChatColor.stripColor("[" + date + " Error]"), true);
|
||||
logs.println(ChatColor.stripColor("[" + date + " Error]"));
|
||||
super.println(ERROR_RED + ChatColor.stripColor("[" + date + " Error]") + RESET_COLOR);
|
||||
console.unstashLine();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void println(boolean x) {
|
||||
console.stashLine();
|
||||
String date = new SimpleDateFormat("HH':'mm':'ss").format(new Date());
|
||||
ConsoleTextOutput.appendText(ChatColor.stripColor("[" + date + " Error] " + x), true);
|
||||
logs.println(ChatColor.stripColor("[" + date + " Error]" + x));
|
||||
super.println(ERROR_RED + ChatColor.stripColor("[" + date + " Error]" + x) + RESET_COLOR);
|
||||
console.unstashLine();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void println(char x) {
|
||||
console.stashLine();
|
||||
String date = new SimpleDateFormat("HH':'mm':'ss").format(new Date());
|
||||
ConsoleTextOutput.appendText(ChatColor.stripColor("[" + date + " Error] " + x), true);
|
||||
logs.println(ChatColor.stripColor("[" + date + " Error]" + x));
|
||||
super.println(ERROR_RED + ChatColor.stripColor("[" + date + " Error]" + x) + RESET_COLOR);
|
||||
console.unstashLine();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void println(char[] x) {
|
||||
console.stashLine();
|
||||
String date = new SimpleDateFormat("HH':'mm':'ss").format(new Date());
|
||||
ConsoleTextOutput.appendText(ChatColor.stripColor("[" + date + " Error] " + String.valueOf(x)), true);
|
||||
logs.println(ChatColor.stripColor("[" + date + " Error]" + String.valueOf(x)));
|
||||
super.println(ERROR_RED + ChatColor.stripColor("[" + date + " Error]" + String.valueOf(x)) + RESET_COLOR);
|
||||
console.unstashLine();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void println(double x) {
|
||||
console.stashLine();
|
||||
String date = new SimpleDateFormat("HH':'mm':'ss").format(new Date());
|
||||
ConsoleTextOutput.appendText(ChatColor.stripColor("[" + date + " Error] " + x), true);
|
||||
logs.println(ChatColor.stripColor("[" + date + " Error]" + x));
|
||||
super.println(ERROR_RED + ChatColor.stripColor("[" + date + " Error]" + x) + RESET_COLOR);
|
||||
console.unstashLine();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void println(float x) {
|
||||
console.stashLine();
|
||||
String date = new SimpleDateFormat("HH':'mm':'ss").format(new Date());
|
||||
ConsoleTextOutput.appendText(ChatColor.stripColor("[" + date + " Error] " + x), true);
|
||||
logs.println(ChatColor.stripColor("[" + date + " Error]" + x));
|
||||
super.println(ERROR_RED + ChatColor.stripColor("[" + date + " Error]" + x) + RESET_COLOR);
|
||||
console.unstashLine();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void println(int x) {
|
||||
console.stashLine();
|
||||
String date = new SimpleDateFormat("HH':'mm':'ss").format(new Date());
|
||||
ConsoleTextOutput.appendText(ChatColor.stripColor("[" + date + " Error] " + x), true);
|
||||
logs.println(ChatColor.stripColor("[" + date + " Error]" + x));
|
||||
super.println(ERROR_RED + ChatColor.stripColor("[" + date + " Error]" + x) + RESET_COLOR);
|
||||
console.unstashLine();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void println(long x) {
|
||||
console.stashLine();
|
||||
String date = new SimpleDateFormat("HH':'mm':'ss").format(new Date());
|
||||
ConsoleTextOutput.appendText(ChatColor.stripColor("[" + date + " Error] " + x), true);
|
||||
logs.println(ChatColor.stripColor("[" + date + " Error]" + x));
|
||||
super.println(ERROR_RED + ChatColor.stripColor("[" + date + " Error]" + x) + RESET_COLOR);
|
||||
console.unstashLine();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void println(Object x) {
|
||||
console.stashLine();
|
||||
String date = new SimpleDateFormat("HH':'mm':'ss").format(new Date());
|
||||
ConsoleTextOutput.appendText(ChatColor.stripColor("[" + date + " Error] " + x), true);
|
||||
logs.println(ChatColor.stripColor("[" + date + " Error]" + x));
|
||||
super.println(ERROR_RED + ChatColor.stripColor("[" + date + " Error]" + x) + RESET_COLOR);
|
||||
console.unstashLine();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void println(String string) {
|
||||
console.stashLine();
|
||||
String date = new SimpleDateFormat("HH':'mm':'ss").format(new Date());
|
||||
ConsoleTextOutput.appendText(ChatColor.stripColor("[" + date + " Error] " + string), true);
|
||||
logs.println(ChatColor.stripColor("[" + date + " Error] " + string));
|
||||
super.println(ERROR_RED + ChatColor.stripColor("[" + date + " Error] " + string) + RESET_COLOR);
|
||||
console.unstashLine();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
package com.loohp.limbo;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.List;
|
||||
|
||||
import com.loohp.limbo.Commands.CommandSender;
|
||||
import com.loohp.limbo.Server.Packets.PacketPlayOutDeclareCommands;
|
||||
import com.loohp.limbo.Utils.DataTypeIO;
|
||||
|
||||
public class DeclareCommands {
|
||||
|
||||
public static PacketPlayOutDeclareCommands getDeclareCommandsPacket(CommandSender sender) throws IOException {
|
||||
List<String> commands = Limbo.getInstance().getPluginManager().getTabOptions(sender, new String[0]);
|
||||
|
||||
if (commands.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
|
||||
DataOutputStream output = new DataOutputStream(buffer);
|
||||
|
||||
DataTypeIO.writeVarInt(output, commands.size() * 2 + 1);
|
||||
|
||||
output.writeByte(0);
|
||||
DataTypeIO.writeVarInt(output, commands.size());
|
||||
for (int i = 1; i <= commands.size() * 2; i++) {
|
||||
DataTypeIO.writeVarInt(output, i++);
|
||||
}
|
||||
|
||||
int i = 1;
|
||||
for (String label : commands) {
|
||||
output.writeByte(1 | 0x04);
|
||||
DataTypeIO.writeVarInt(output, 1);
|
||||
DataTypeIO.writeVarInt(output, i + 1);
|
||||
DataTypeIO.writeString(output, label, StandardCharsets.UTF_8);
|
||||
i++;
|
||||
|
||||
output.writeByte(2 | 0x04 | 0x10);
|
||||
DataTypeIO.writeVarInt(output, 1);
|
||||
DataTypeIO.writeVarInt(output, i);
|
||||
DataTypeIO.writeString(output, "arg", StandardCharsets.UTF_8);
|
||||
DataTypeIO.writeString(output, "brigadier:string", StandardCharsets.UTF_8);
|
||||
DataTypeIO.writeVarInt(output, 0);
|
||||
DataTypeIO.writeString(output, "minecraft:ask_server", StandardCharsets.UTF_8);
|
||||
i++;
|
||||
}
|
||||
|
||||
DataTypeIO.writeVarInt(output, 0);
|
||||
|
||||
return new PacketPlayOutDeclareCommands(buffer.toByteArray());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package com.loohp.limbo.Events;
|
||||
|
||||
public interface Cancellable {
|
||||
|
||||
public void setCancelled(boolean cancelled);
|
||||
|
||||
public boolean isCancelled();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package com.loohp.limbo.Events;
|
||||
|
||||
public abstract class Event {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.loohp.limbo.Events;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Documented
|
||||
@Target(ElementType.METHOD)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface EventHandler {
|
||||
EventPriority priority() default EventPriority.NORMAL;
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package com.loohp.limbo.Events;
|
||||
|
||||
public enum EventPriority {
|
||||
LOWEST(0),
|
||||
LOW(1),
|
||||
NORMAL(2),
|
||||
HIGH(3),
|
||||
HIGHEST(4),
|
||||
MONITOR(5);
|
||||
|
||||
int order;
|
||||
|
||||
EventPriority(int order) {
|
||||
this.order = order;
|
||||
}
|
||||
|
||||
public int getOrder() {
|
||||
return order;
|
||||
}
|
||||
|
||||
public static EventPriority getByOrder(int order) {
|
||||
for (EventPriority each : EventPriority.values()) {
|
||||
if (each.getOrder() == order) {
|
||||
return each;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static EventPriority[] getPrioritiesInOrder() {
|
||||
EventPriority[] array = new EventPriority[EventPriority.values().length];
|
||||
for (int i = 0; i < array.length; i++) {
|
||||
array[i] = EventPriority.getByOrder(i);
|
||||
}
|
||||
return array;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
package com.loohp.limbo.Events;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import com.loohp.limbo.Plugins.LimboPlugin;
|
||||
|
||||
public class EventsManager {
|
||||
|
||||
private List<ListenerPair> listeners;
|
||||
|
||||
public EventsManager() {
|
||||
listeners = new ArrayList<>();
|
||||
}
|
||||
|
||||
public <T extends Event> T callEvent(T event) {
|
||||
for (EventPriority priority : EventPriority.getPrioritiesInOrder()) {
|
||||
for (ListenerPair entry : listeners) {
|
||||
Listener listener = entry.listener;
|
||||
for (Method method : listener.getClass().getMethods()) {
|
||||
if (method.isAnnotationPresent(EventHandler.class)) {
|
||||
if (method.getAnnotation(EventHandler.class).priority().equals(priority)) {
|
||||
if (method.getParameterCount() == 1 && method.getParameterTypes()[0].equals(event.getClass())) {
|
||||
try {
|
||||
method.invoke(listener, event);
|
||||
} catch (Exception e) {
|
||||
System.err.println("Error while passing " + event.getClass().getCanonicalName() + " to the plugin \"" + entry.plugin.getName() + "\"");
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return event;
|
||||
}
|
||||
|
||||
public void registerEvents(LimboPlugin plugin, Listener listener) {
|
||||
listeners.add(new ListenerPair(plugin, listener));
|
||||
}
|
||||
|
||||
public void unregisterAllListeners(LimboPlugin plugin) {
|
||||
listeners.removeIf(each -> each.plugin.equals(plugin));
|
||||
}
|
||||
|
||||
protected static class ListenerPair {
|
||||
public LimboPlugin plugin;
|
||||
public Listener listener;
|
||||
|
||||
public ListenerPair(LimboPlugin plugin, Listener listener) {
|
||||
this.plugin = plugin;
|
||||
this.listener = listener;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package com.loohp.limbo.Events;
|
||||
|
||||
public interface Listener {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package com.loohp.limbo.Events;
|
||||
|
||||
import com.loohp.limbo.Player.Player;
|
||||
|
||||
public class PlayerChatEvent extends PlayerEvent implements Cancellable {
|
||||
|
||||
private String prefix;
|
||||
private String message;
|
||||
private boolean cancelled;
|
||||
|
||||
public PlayerChatEvent(Player player, String prefix, String message, boolean cancelled) {
|
||||
super(player);
|
||||
this.prefix = prefix;
|
||||
this.message = message;
|
||||
this.cancelled = cancelled;
|
||||
}
|
||||
|
||||
public String getPrefix() {
|
||||
return prefix;
|
||||
}
|
||||
|
||||
public void setPrefix(String prefix) {
|
||||
this.prefix = prefix;
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
public void setMessage(String message) {
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCancelled(boolean cancelled) {
|
||||
this.cancelled = cancelled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCancelled() {
|
||||
return cancelled;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package com.loohp.limbo.Events;
|
||||
|
||||
import com.loohp.limbo.Player.Player;
|
||||
|
||||
public class PlayerEvent extends Event {
|
||||
|
||||
private Player player;
|
||||
|
||||
public PlayerEvent(Player player) {
|
||||
this.player = player;
|
||||
}
|
||||
|
||||
public Player getPlayer() {
|
||||
return player;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package com.loohp.limbo.Events;
|
||||
|
||||
import com.loohp.limbo.Player.Player;
|
||||
|
||||
public class PlayerJoinEvent extends PlayerEvent {
|
||||
|
||||
public PlayerJoinEvent(Player player) {
|
||||
super(player);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package com.loohp.limbo.Events;
|
||||
|
||||
import com.loohp.limbo.Server.ClientConnection;
|
||||
|
||||
import net.md_5.bungee.api.chat.BaseComponent;
|
||||
|
||||
public class PlayerLoginEvent extends Event implements Cancellable {
|
||||
|
||||
private ClientConnection connection;
|
||||
private boolean cancelled;
|
||||
private BaseComponent[] cancelReason;
|
||||
|
||||
public PlayerLoginEvent(ClientConnection connection, boolean cancelled, BaseComponent... cancelReason) {
|
||||
this.connection = connection;
|
||||
this.cancelled = cancelled;
|
||||
this.cancelReason = cancelReason;
|
||||
}
|
||||
|
||||
public ClientConnection getConnection() {
|
||||
return connection;
|
||||
}
|
||||
|
||||
public BaseComponent[] getCancelReason() {
|
||||
return cancelReason;
|
||||
}
|
||||
|
||||
public void setCancelReason(BaseComponent... cancelReason) {
|
||||
this.cancelReason = cancelReason;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCancelled(boolean cancelled) {
|
||||
this.cancelled = cancelled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCancelled() {
|
||||
return cancelled;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
package com.loohp.limbo.Events;
|
||||
|
||||
import com.loohp.limbo.Location.Location;
|
||||
import com.loohp.limbo.Player.Player;
|
||||
|
||||
/**
|
||||
* Holds information for player movement events
|
||||
*/
|
||||
public class PlayerMoveEvent extends PlayerEvent implements Cancellable {
|
||||
|
||||
private boolean cancel = false;
|
||||
private Location from;
|
||||
private Location to;
|
||||
|
||||
public PlayerMoveEvent(Player player, Location from, Location to) {
|
||||
super(player);
|
||||
this.from = from;
|
||||
this.to = to;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the cancellation state of this event. A cancelled event will not
|
||||
* be executed in the server, but will still pass to other plugins
|
||||
* <p>
|
||||
* If a move or teleport event is cancelled, the player will be moved or
|
||||
* teleported back to the Location as defined by getFrom(). This will not
|
||||
* fire an event
|
||||
*
|
||||
* @return true if this event is cancelled
|
||||
*/
|
||||
@Override
|
||||
public boolean isCancelled() {
|
||||
return cancel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the cancellation state of this event. A cancelled event will not
|
||||
* be executed in the server, but will still pass to other plugins
|
||||
* <p>
|
||||
* If a move or teleport event is cancelled, the player will be moved or
|
||||
* teleported back to the Location as defined by getFrom(). This will not
|
||||
* fire an event
|
||||
*
|
||||
* @param cancel true if you wish to cancel this event
|
||||
*/
|
||||
@Override
|
||||
public void setCancelled(boolean cancel) {
|
||||
this.cancel = cancel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the location this player moved from
|
||||
*
|
||||
* @return Location the player moved from
|
||||
*/
|
||||
public Location getFrom() {
|
||||
return from;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the location to mark as where the player moved from
|
||||
*
|
||||
* @param from New location to mark as the players previous location
|
||||
*/
|
||||
public void setFrom(Location from) {
|
||||
this.from = from;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the location this player moved to
|
||||
*
|
||||
* @return Location the player moved to
|
||||
*/
|
||||
public Location getTo() {
|
||||
return to;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the location that this player will move to
|
||||
*
|
||||
* @param to New Location this player will move to
|
||||
*/
|
||||
public void setTo(Location to) {
|
||||
this.to = to;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package com.loohp.limbo.Events;
|
||||
|
||||
import com.loohp.limbo.Player.Player;
|
||||
|
||||
public class PlayerQuitEvent extends PlayerEvent {
|
||||
|
||||
public PlayerQuitEvent(Player player) {
|
||||
super(player);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package com.loohp.limbo.Events;
|
||||
|
||||
import com.loohp.limbo.Location.Location;
|
||||
import com.loohp.limbo.Player.Player;
|
||||
|
||||
public class PlayerTeleportEvent extends PlayerMoveEvent {
|
||||
|
||||
public PlayerTeleportEvent(Player player, Location from, Location to) {
|
||||
super(player, from, to);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
package com.loohp.limbo.Events;
|
||||
|
||||
import java.awt.image.BufferedImage;
|
||||
|
||||
import com.loohp.limbo.Server.ClientConnection;
|
||||
|
||||
import net.md_5.bungee.api.chat.BaseComponent;
|
||||
|
||||
public class StatusPingEvent extends Event {
|
||||
|
||||
private ClientConnection connection;
|
||||
private String version;
|
||||
private int protocol;
|
||||
private BaseComponent[] motd;
|
||||
private int maxPlayers;
|
||||
private int playersOnline;
|
||||
private BufferedImage favicon;
|
||||
|
||||
public StatusPingEvent(ClientConnection connection, String version, int protocol, BaseComponent[] motd, int maxPlayers, int playersOnline, BufferedImage favicon) {
|
||||
this.connection = connection;
|
||||
this.version = version;
|
||||
this.protocol = protocol;
|
||||
this.motd = motd;
|
||||
this.maxPlayers = maxPlayers;
|
||||
this.playersOnline = playersOnline;
|
||||
this.favicon = favicon;
|
||||
}
|
||||
|
||||
public ClientConnection getConnection() {
|
||||
return connection;
|
||||
}
|
||||
|
||||
public String getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
public void setVersion(String version) {
|
||||
this.version = version;
|
||||
}
|
||||
|
||||
public int getProtocol() {
|
||||
return protocol;
|
||||
}
|
||||
|
||||
public void setProtocol(int protocol) {
|
||||
this.protocol = protocol;
|
||||
}
|
||||
|
||||
public BaseComponent[] getMotd() {
|
||||
return motd;
|
||||
}
|
||||
|
||||
public void setMotd(BaseComponent[] motd) {
|
||||
this.motd = motd;
|
||||
}
|
||||
|
||||
public int getMaxPlayers() {
|
||||
return maxPlayers;
|
||||
}
|
||||
|
||||
public void setMaxPlayers(int maxPlayers) {
|
||||
this.maxPlayers = maxPlayers;
|
||||
}
|
||||
|
||||
public int getPlayersOnline() {
|
||||
return playersOnline;
|
||||
}
|
||||
|
||||
public void setPlayersOnline(int playersOnline) {
|
||||
this.playersOnline = playersOnline;
|
||||
}
|
||||
|
||||
public BufferedImage getFavicon() {
|
||||
return favicon;
|
||||
}
|
||||
|
||||
public void setFavicon(BufferedImage favicon) {
|
||||
this.favicon = favicon;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
package com.loohp.limbo.File;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.InputStream;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.yaml.snakeyaml.DumperOptions;
|
||||
import org.yaml.snakeyaml.Yaml;
|
||||
import org.yaml.snakeyaml.representer.Representer;
|
||||
|
||||
import com.loohp.limbo.Utils.YamlOrder;
|
||||
|
||||
public class FileConfiguration {
|
||||
|
||||
File file;
|
||||
|
||||
Map<String, Object> mapping;
|
||||
String header;
|
||||
|
||||
public FileConfiguration(File file) throws FileNotFoundException {
|
||||
this.file = file;
|
||||
if (file.exists()) {
|
||||
reloadConfig();
|
||||
} else {
|
||||
mapping = new LinkedHashMap<>();
|
||||
}
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public FileConfiguration(InputStream input){
|
||||
reloadConfig(input);
|
||||
}
|
||||
|
||||
public FileConfiguration reloadConfig() throws FileNotFoundException {
|
||||
return reloadConfig(new FileInputStream(file));
|
||||
}
|
||||
|
||||
private FileConfiguration reloadConfig(InputStream input) {
|
||||
Yaml yml = new Yaml();
|
||||
mapping = yml.load(input);
|
||||
return this;
|
||||
}
|
||||
|
||||
public void setHeader(String header) {
|
||||
this.header = header;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> T get(String key, Class<T> returnType) {
|
||||
try {
|
||||
String[] tree = key.split("\\.");
|
||||
Map<String, Object> map = mapping;
|
||||
for (int i = 0; i < tree.length - 1; i++) {
|
||||
map = (Map<String, Object>) map.get(tree[i]);
|
||||
}
|
||||
if (returnType.equals(String.class)) {
|
||||
return (T) map.get(tree[tree.length - 1]).toString();
|
||||
}
|
||||
return returnType.cast(map.get(tree[tree.length - 1]));
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> void set(String key, T value) {
|
||||
String[] tree = key.split("\\.");
|
||||
Map<String, Object> map = mapping;
|
||||
for (int i = 0; i < tree.length - 1; i++) {
|
||||
Map<String, Object> map1 = (Map<String, Object>) map.get(tree[i]);
|
||||
if (map1 == null) {
|
||||
map1 = new LinkedHashMap<>();
|
||||
map.put(tree[i], map1);
|
||||
}
|
||||
map = map1;
|
||||
}
|
||||
if (value != null) {
|
||||
map.put(tree[tree.length - 1], (T) value);
|
||||
} else {
|
||||
map.remove(tree[tree.length - 1]);
|
||||
}
|
||||
}
|
||||
|
||||
public void saveConfig(File file) throws FileNotFoundException, UnsupportedEncodingException {
|
||||
DumperOptions options = new DumperOptions();
|
||||
options.setIndent(2);
|
||||
options.setPrettyFlow(true);
|
||||
options.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
|
||||
Representer customRepresenter = new Representer();
|
||||
YamlOrder customProperty = new YamlOrder();
|
||||
customRepresenter.setPropertyUtils(customProperty);
|
||||
Yaml yaml = new Yaml(customRepresenter, options);
|
||||
|
||||
if (file.getParentFile() != null) {
|
||||
file.getParentFile().mkdirs();
|
||||
}
|
||||
|
||||
PrintWriter pw = new PrintWriter(file, StandardCharsets.UTF_8.toString());
|
||||
if (header != null) {
|
||||
pw.println("#" + header.replace("\n", "\n#"));
|
||||
}
|
||||
yaml.dump(mapping, pw);
|
||||
pw.flush();
|
||||
pw.close();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,159 @@
|
||||
package com.loohp.limbo.File;
|
||||
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.Optional;
|
||||
import java.util.Properties;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
|
||||
import com.loohp.limbo.Limbo;
|
||||
import com.loohp.limbo.Location.Location;
|
||||
import com.loohp.limbo.Utils.GameMode;
|
||||
import com.loohp.limbo.Utils.NamespacedKey;
|
||||
import com.loohp.limbo.World.World;
|
||||
|
||||
public class ServerProperties {
|
||||
|
||||
File file;
|
||||
int maxPlayers;
|
||||
int serverPort;
|
||||
String serverIp;
|
||||
NamespacedKey levelName;
|
||||
String schemFileName;
|
||||
NamespacedKey levelDimension;
|
||||
GameMode defaultGamemode;
|
||||
Location worldSpawn;
|
||||
boolean reducedDebugInfo;
|
||||
boolean allowFlight;
|
||||
String motdJson;
|
||||
String versionString;
|
||||
int protocol;
|
||||
boolean bungeecord;
|
||||
|
||||
Optional<BufferedImage> favicon;
|
||||
|
||||
public ServerProperties(File file) throws IOException {
|
||||
this.file = file;
|
||||
Properties prop = new Properties();
|
||||
prop.load(new FileInputStream(file));
|
||||
|
||||
protocol = Limbo.getInstance().serverImplmentationProtocol;
|
||||
|
||||
maxPlayers = Integer.parseInt(prop.getProperty("max-players"));
|
||||
serverPort = Integer.parseInt(prop.getProperty("server-port"));
|
||||
serverIp = prop.getProperty("server-ip");
|
||||
String[] level = prop.getProperty("level-name").split(";");
|
||||
levelName = new NamespacedKey(level[0]);
|
||||
schemFileName = level[1];
|
||||
levelDimension = new NamespacedKey(prop.getProperty("level-dimension"));
|
||||
defaultGamemode = GameMode.fromName(new NamespacedKey(prop.getProperty("default-gamemode")).getKey());
|
||||
String[] locStr = prop.getProperty("world-spawn").split(";");
|
||||
World world = Limbo.getInstance().getWorld(locStr[0]);
|
||||
double x = Double.parseDouble(locStr[1]);
|
||||
double y = Double.parseDouble(locStr[2]);
|
||||
double z = Double.parseDouble(locStr[3]);
|
||||
float yaw = Float.parseFloat(locStr[4]);
|
||||
float pitch = Float.parseFloat(locStr[5]);
|
||||
worldSpawn = new Location(world, x, y, z, yaw, pitch);
|
||||
reducedDebugInfo = Boolean.parseBoolean(prop.getProperty("reduced-debug-info"));
|
||||
allowFlight = Boolean.parseBoolean(prop.getProperty("allow-flight"));
|
||||
motdJson = prop.getProperty("motd");
|
||||
versionString = prop.getProperty("version");
|
||||
bungeecord = Boolean.parseBoolean(prop.getProperty("bungeecord"));
|
||||
|
||||
File png = new File("server-icon.png");
|
||||
if (png.exists()) {
|
||||
try {
|
||||
BufferedImage image = ImageIO.read(png);
|
||||
if (image.getHeight() == 64 && image.getWidth() == 64) {
|
||||
favicon = Optional.of(image);
|
||||
} else {
|
||||
Limbo.getInstance().getConsole().sendMessage("Unable to load server-icon.png! The image is not 64 x 64 in size!");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Limbo.getInstance().getConsole().sendMessage("Unable to load server-icon.png! Is it a png image?");
|
||||
}
|
||||
} else {
|
||||
Limbo.getInstance().getConsole().sendMessage("No server-icon.png found");
|
||||
favicon = Optional.empty();
|
||||
}
|
||||
|
||||
Limbo.getInstance().getConsole().sendMessage("Loaded server.properties");
|
||||
}
|
||||
|
||||
public String getServerImplementationVersion() {
|
||||
return Limbo.getInstance().serverImplementationVersion;
|
||||
}
|
||||
|
||||
public boolean isBungeecord() {
|
||||
return bungeecord;
|
||||
}
|
||||
|
||||
public Optional<BufferedImage> getFavicon() {
|
||||
return favicon;
|
||||
}
|
||||
|
||||
public File getFile() {
|
||||
return file;
|
||||
}
|
||||
|
||||
public int getMaxPlayers() {
|
||||
return maxPlayers;
|
||||
}
|
||||
|
||||
public int getServerPort() {
|
||||
return serverPort;
|
||||
}
|
||||
|
||||
public String getServerIp() {
|
||||
return serverIp;
|
||||
}
|
||||
|
||||
public NamespacedKey getLevelName() {
|
||||
return levelName;
|
||||
}
|
||||
|
||||
public String getSchemFileName() {
|
||||
return schemFileName;
|
||||
}
|
||||
|
||||
public NamespacedKey getLevelDimension() {
|
||||
return levelDimension;
|
||||
}
|
||||
|
||||
public GameMode getDefaultGamemode() {
|
||||
return defaultGamemode;
|
||||
}
|
||||
|
||||
public Location getWorldSpawn() {
|
||||
return worldSpawn;
|
||||
}
|
||||
|
||||
public void setWorldSpawn(Location location) {
|
||||
this.worldSpawn = location;
|
||||
}
|
||||
|
||||
public boolean isReducedDebugInfo() {
|
||||
return reducedDebugInfo;
|
||||
}
|
||||
|
||||
public boolean isAllowFlight() {
|
||||
return allowFlight;
|
||||
}
|
||||
|
||||
public String getMotdJson() {
|
||||
return motdJson;
|
||||
}
|
||||
|
||||
public String getVersionString() {
|
||||
return versionString;
|
||||
}
|
||||
|
||||
public int getProtocol() {
|
||||
return protocol;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package com.loohp.limbo.GUI;
|
||||
|
||||
import com.loohp.limbo.Limbo;
|
||||
|
||||
public class ConsoleTextOutput {
|
||||
|
||||
public static void appendText(String string) {
|
||||
if (!Limbo.noGui) {
|
||||
GUI.textOutput.setText(GUI.textOutput.getText() + string);
|
||||
GUI.scrollPane.getVerticalScrollBar().setValue(GUI.scrollPane.getVerticalScrollBar().getMaximum());
|
||||
}
|
||||
}
|
||||
|
||||
public static void appendText(String string, boolean isWriteLine) {
|
||||
if (isWriteLine) {
|
||||
appendText(string + "\n");
|
||||
} else {
|
||||
appendText(string);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,218 @@
|
||||
package com.loohp.limbo.GUI;
|
||||
|
||||
import java.awt.Font;
|
||||
import java.awt.GridBagConstraints;
|
||||
import java.awt.GridBagLayout;
|
||||
import java.awt.Insets;
|
||||
import java.awt.event.KeyAdapter;
|
||||
import java.awt.event.KeyEvent;
|
||||
import java.awt.event.MouseAdapter;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.awt.event.WindowAdapter;
|
||||
import java.awt.event.WindowEvent;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JFrame;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JScrollPane;
|
||||
import javax.swing.JTextField;
|
||||
import javax.swing.JTextPane;
|
||||
import javax.swing.border.EmptyBorder;
|
||||
|
||||
import com.loohp.limbo.Limbo;
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
public class GUI extends JFrame {
|
||||
|
||||
public static JPanel contentPane;
|
||||
public static JTextField commandInput;
|
||||
public static JButton execCommand;
|
||||
public static JTextPane textOutput;
|
||||
public static JScrollPane scrollPane;
|
||||
public static JLabel consoleLabel;
|
||||
public static JLabel clientLabel;
|
||||
public static JTextPane clientText;
|
||||
public static JScrollPane scrollPane_client;
|
||||
public static JLabel sysLabel;
|
||||
public static JScrollPane scrollPane_sys;
|
||||
public static JTextPane sysText;
|
||||
|
||||
public static List<String> history = new ArrayList<String>();
|
||||
public static int currenthistory = 0;
|
||||
|
||||
public static boolean loadFinish = false;
|
||||
|
||||
/**
|
||||
* Launch the application.
|
||||
*/
|
||||
public static void main() {
|
||||
GUI frame = new GUI();
|
||||
frame.setVisible(true);
|
||||
|
||||
Thread t1 = new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
SystemInfo.printInfo();
|
||||
}
|
||||
});
|
||||
t1.start();
|
||||
|
||||
loadFinish = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the frame.
|
||||
*/
|
||||
public GUI() {
|
||||
setTitle("Limbo Minecraft Server");
|
||||
addWindowListener(new WindowAdapter() {
|
||||
@Override
|
||||
public void windowClosing(WindowEvent e) {
|
||||
Limbo.getInstance().stopServer();
|
||||
}
|
||||
});
|
||||
setBounds(100, 100, 1198, 686);
|
||||
contentPane = new JPanel();
|
||||
contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
|
||||
setContentPane(contentPane);
|
||||
GridBagLayout gbl_contentPane = new GridBagLayout();
|
||||
gbl_contentPane.columnWidths = new int[]{243, 10, 36, 111, 0};
|
||||
gbl_contentPane.rowHeights = new int[]{0, 160, 0, 10, 33, 33, 0};
|
||||
gbl_contentPane.columnWeights = new double[]{0.0, 0.0, 1.0, 0.0, Double.MIN_VALUE};
|
||||
gbl_contentPane.rowWeights = new double[]{0.0, 0.0, 0.0, 0.0, 1.0, 0.0, Double.MIN_VALUE};
|
||||
contentPane.setLayout(gbl_contentPane);
|
||||
|
||||
sysLabel = new JLabel("System Information");
|
||||
sysLabel.setFont(new Font("Arial", Font.BOLD, 11));
|
||||
GridBagConstraints gbc_sysLabel = new GridBagConstraints();
|
||||
gbc_sysLabel.fill = GridBagConstraints.BOTH;
|
||||
gbc_sysLabel.insets = new Insets(0, 0, 5, 5);
|
||||
gbc_sysLabel.gridx = 0;
|
||||
gbc_sysLabel.gridy = 0;
|
||||
contentPane.add(sysLabel, gbc_sysLabel);
|
||||
|
||||
consoleLabel = new JLabel("Console Output");
|
||||
consoleLabel.setFont(new Font("Arial", Font.BOLD, 11));
|
||||
GridBagConstraints gbc_consoleLabel = new GridBagConstraints();
|
||||
gbc_consoleLabel.anchor = GridBagConstraints.WEST;
|
||||
gbc_consoleLabel.insets = new Insets(0, 0, 5, 5);
|
||||
gbc_consoleLabel.gridx = 2;
|
||||
gbc_consoleLabel.gridy = 0;
|
||||
contentPane.add(consoleLabel, gbc_consoleLabel);
|
||||
|
||||
commandInput = new JTextField();
|
||||
commandInput.setToolTipText("Input a command");
|
||||
commandInput.addKeyListener(new KeyAdapter() {
|
||||
@Override
|
||||
public void keyPressed(KeyEvent e) {
|
||||
if (e.getKeyCode() == 10) {
|
||||
String cmd = commandInput.getText();
|
||||
if (!commandInput.getText().equals("")) {
|
||||
history.add(cmd);
|
||||
currenthistory = history.size();
|
||||
}
|
||||
Limbo.getInstance().dispatchCommand(Limbo.getInstance().getConsole(), cmd.trim().replaceAll(" +", " "));
|
||||
commandInput.setText("");
|
||||
} else if (e.getKeyCode() == 38) {
|
||||
currenthistory--;
|
||||
if (currenthistory >= 0) {
|
||||
commandInput.setText(history.get(currenthistory));
|
||||
} else {
|
||||
currenthistory++;
|
||||
}
|
||||
} else if (e.getKeyCode() == 40) {
|
||||
currenthistory++;
|
||||
if (currenthistory < history.size()) {
|
||||
commandInput.setText(history.get(currenthistory));
|
||||
} else {
|
||||
currenthistory--;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
scrollPane_sys = new JScrollPane();
|
||||
GridBagConstraints gbc_scrollPane_sys = new GridBagConstraints();
|
||||
gbc_scrollPane_sys.insets = new Insets(0, 0, 5, 5);
|
||||
gbc_scrollPane_sys.fill = GridBagConstraints.BOTH;
|
||||
gbc_scrollPane_sys.gridx = 0;
|
||||
gbc_scrollPane_sys.gridy = 1;
|
||||
contentPane.add(scrollPane_sys, gbc_scrollPane_sys);
|
||||
|
||||
sysText = new JTextPane();
|
||||
sysText.setFont(new Font("Consolas", Font.PLAIN, 12));
|
||||
sysText.setEditable(false);
|
||||
scrollPane_sys.setViewportView(sysText);
|
||||
|
||||
clientLabel = new JLabel("Connected Clients");
|
||||
clientLabel.setFont(new Font("Arial", Font.BOLD, 11));
|
||||
GridBagConstraints gbc_clientLabel = new GridBagConstraints();
|
||||
gbc_clientLabel.anchor = GridBagConstraints.WEST;
|
||||
gbc_clientLabel.insets = new Insets(0, 0, 5, 5);
|
||||
gbc_clientLabel.gridx = 0;
|
||||
gbc_clientLabel.gridy = 3;
|
||||
contentPane.add(clientLabel, gbc_clientLabel);
|
||||
|
||||
scrollPane_client = new JScrollPane();
|
||||
GridBagConstraints gbc_scrollPane_client = new GridBagConstraints();
|
||||
gbc_scrollPane_client.fill = GridBagConstraints.BOTH;
|
||||
gbc_scrollPane_client.gridheight = 2;
|
||||
gbc_scrollPane_client.insets = new Insets(0, 0, 0, 5);
|
||||
gbc_scrollPane_client.gridx = 0;
|
||||
gbc_scrollPane_client.gridy = 4;
|
||||
contentPane.add(scrollPane_client, gbc_scrollPane_client);
|
||||
|
||||
clientText = new JTextPane();
|
||||
scrollPane_client.setViewportView(clientText);
|
||||
clientText.setFont(new Font("Consolas", Font.PLAIN, 12));
|
||||
clientText.setEditable(false);
|
||||
|
||||
scrollPane = new JScrollPane();
|
||||
GridBagConstraints gbc_scrollPane = new GridBagConstraints();
|
||||
gbc_scrollPane.gridheight = 4;
|
||||
gbc_scrollPane.insets = new Insets(0, 0, 5, 0);
|
||||
gbc_scrollPane.gridwidth = 2;
|
||||
gbc_scrollPane.fill = GridBagConstraints.BOTH;
|
||||
gbc_scrollPane.gridx = 2;
|
||||
gbc_scrollPane.gridy = 1;
|
||||
contentPane.add(scrollPane, gbc_scrollPane);
|
||||
|
||||
textOutput = new JTextPane();
|
||||
scrollPane.setViewportView(textOutput);
|
||||
textOutput.setFont(new Font("Consolas", Font.PLAIN, 12));
|
||||
textOutput.setEditable(false);
|
||||
commandInput.setFont(new Font("Tahoma", Font.PLAIN, 19));
|
||||
GridBagConstraints gbc_commandInput = new GridBagConstraints();
|
||||
gbc_commandInput.insets = new Insets(0, 0, 0, 5);
|
||||
gbc_commandInput.fill = GridBagConstraints.BOTH;
|
||||
gbc_commandInput.gridx = 2;
|
||||
gbc_commandInput.gridy = 5;
|
||||
contentPane.add(commandInput, gbc_commandInput);
|
||||
commandInput.setColumns(10);
|
||||
|
||||
execCommand = new JButton("RUN");
|
||||
execCommand.setToolTipText("Execute a command");
|
||||
execCommand.addMouseListener(new MouseAdapter() {
|
||||
@Override
|
||||
public void mouseReleased(MouseEvent e) {
|
||||
String cmd = commandInput.getText();
|
||||
if (!commandInput.getText().equals("")) {
|
||||
history.add(cmd);
|
||||
currenthistory = history.size();
|
||||
}
|
||||
Limbo.getInstance().dispatchCommand(Limbo.getInstance().getConsole(), cmd.trim().replaceAll(" +", " "));
|
||||
commandInput.setText("");
|
||||
}
|
||||
});
|
||||
execCommand.setFont(new Font("Tahoma", Font.PLAIN, 19));
|
||||
GridBagConstraints gbc_execCommand = new GridBagConstraints();
|
||||
gbc_execCommand.fill = GridBagConstraints.BOTH;
|
||||
gbc_execCommand.gridx = 3;
|
||||
gbc_execCommand.gridy = 5;
|
||||
contentPane.add(execCommand, gbc_execCommand);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
package com.loohp.limbo.GUI;
|
||||
|
||||
import java.lang.management.ManagementFactory;
|
||||
import java.text.NumberFormat;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import com.loohp.limbo.Limbo;
|
||||
|
||||
public class SystemInfo {
|
||||
|
||||
public static void printInfo() {
|
||||
if (!Limbo.noGui) {
|
||||
while (true) {
|
||||
Runtime runtime = Runtime.getRuntime();
|
||||
|
||||
NumberFormat format = NumberFormat.getInstance();
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
long maxMemory = runtime.maxMemory();
|
||||
long allocatedMemory = runtime.totalMemory();
|
||||
long freeMemory = runtime.freeMemory();
|
||||
|
||||
sb.append("Free Memory: " + format.format(freeMemory / 1024 / 1024) + " MB\n");
|
||||
sb.append("Allocated Memory: " + format.format(allocatedMemory / 1024 / 1024) + " MB\n");
|
||||
sb.append("Max Memory: " + format.format(maxMemory / 1024 / 1024) + " MB\n");
|
||||
sb.append("Memory Usage: " + format.format((allocatedMemory - freeMemory) / 1024 / 1024) + "/" + format.format(maxMemory / 1024 / 1024) + " MB (" + Math.round((double) (allocatedMemory - freeMemory) / (double) (maxMemory) * 100) + "%)\n");
|
||||
sb.append("\n");
|
||||
|
||||
try {
|
||||
@SuppressWarnings("restriction")
|
||||
com.sun.management.OperatingSystemMXBean operatingSystemMXBean = (com.sun.management.OperatingSystemMXBean) ManagementFactory.getOperatingSystemMXBean();
|
||||
@SuppressWarnings("restriction")
|
||||
double processLoad = operatingSystemMXBean.getProcessCpuLoad();
|
||||
@SuppressWarnings("restriction")
|
||||
double systemLoad = operatingSystemMXBean.getSystemCpuLoad();
|
||||
int processors = runtime.availableProcessors();
|
||||
|
||||
sb.append("Available Processors: " + processors + "\n");
|
||||
sb.append("Process CPU Load: " + Math.round(processLoad * 100) + "%\n");
|
||||
sb.append("System CPU Load: " + Math.round(systemLoad * 100) + "%\n");
|
||||
GUI.sysText.setText(sb.toString());
|
||||
} catch (Exception ignore) {}
|
||||
|
||||
try {TimeUnit.MILLISECONDS.sleep(1000);} catch (InterruptedException e) {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,519 @@
|
||||
package com.loohp.limbo;
|
||||
|
||||
import java.awt.GraphicsEnvironment;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.FileReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.net.URL;
|
||||
import java.nio.channels.Channels;
|
||||
import java.nio.channels.ReadableByteChannel;
|
||||
import java.nio.file.Files;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Enumeration;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.TreeMap;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.json.simple.JSONArray;
|
||||
import org.json.simple.JSONObject;
|
||||
import org.json.simple.parser.JSONParser;
|
||||
import org.json.simple.parser.ParseException;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.loohp.limbo.Commands.CommandSender;
|
||||
import com.loohp.limbo.Events.EventsManager;
|
||||
import com.loohp.limbo.File.ServerProperties;
|
||||
import com.loohp.limbo.GUI.GUI;
|
||||
import com.loohp.limbo.Location.Location;
|
||||
import com.loohp.limbo.Metrics.Metrics;
|
||||
import com.loohp.limbo.Permissions.PermissionsManager;
|
||||
import com.loohp.limbo.Player.Player;
|
||||
import com.loohp.limbo.Plugins.LimboPlugin;
|
||||
import com.loohp.limbo.Plugins.PluginManager;
|
||||
import com.loohp.limbo.Server.ServerConnection;
|
||||
import com.loohp.limbo.Server.Packets.Packet;
|
||||
import com.loohp.limbo.Server.Packets.PacketIn;
|
||||
import com.loohp.limbo.Server.Packets.PacketOut;
|
||||
import com.loohp.limbo.Utils.CustomStringUtils;
|
||||
import com.loohp.limbo.Utils.ImageUtils;
|
||||
import com.loohp.limbo.Utils.NetworkUtils;
|
||||
import com.loohp.limbo.World.DimensionRegistry;
|
||||
import com.loohp.limbo.World.Environment;
|
||||
import com.loohp.limbo.World.Schematic;
|
||||
import com.loohp.limbo.World.World;
|
||||
|
||||
import net.md_5.bungee.api.chat.BaseComponent;
|
||||
import net.md_5.bungee.chat.ComponentSerializer;
|
||||
import net.querz.nbt.io.NBTUtil;
|
||||
import net.querz.nbt.tag.CompoundTag;
|
||||
|
||||
public class Limbo {
|
||||
|
||||
private static Limbo instance;
|
||||
public static boolean noGui = false;
|
||||
|
||||
public static void main(String args[]) throws IOException, ParseException, NumberFormatException, ClassNotFoundException, InterruptedException {
|
||||
for (String flag : args) {
|
||||
if (flag.equals("--nogui")) {
|
||||
noGui = true;
|
||||
} else {
|
||||
System.out.println("Accepted flags:");
|
||||
System.out.println(" --nogui ");
|
||||
System.out.println();
|
||||
System.out.println("Press [enter] to quit");
|
||||
System.exit(0);
|
||||
}
|
||||
}
|
||||
if (GraphicsEnvironment.isHeadless()) {
|
||||
noGui = true;
|
||||
}
|
||||
if (!noGui) {
|
||||
System.out.println("Launching Server GUI.. Add \"--nogui\" in launch arguments to disable");
|
||||
Thread t1 = new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
GUI.main();
|
||||
}
|
||||
});
|
||||
t1.start();
|
||||
}
|
||||
|
||||
new Limbo();
|
||||
}
|
||||
|
||||
public static Limbo getInstance() {
|
||||
return instance;
|
||||
}
|
||||
|
||||
//===========================
|
||||
|
||||
public final String serverImplementationVersion = "1.16.4";
|
||||
public final int serverImplmentationProtocol = 754;
|
||||
public final String limboImplementationVersion;
|
||||
|
||||
private ServerConnection server;
|
||||
private Console console;
|
||||
|
||||
private List<World> worlds = new ArrayList<>();
|
||||
private Map<String, Player> playersByName = new HashMap<>();
|
||||
private Map<UUID, Player> playersByUUID = new HashMap<>();
|
||||
|
||||
private ServerProperties properties;
|
||||
|
||||
private PluginManager pluginManager;
|
||||
private EventsManager eventsManager;
|
||||
private PermissionsManager permissionManager;
|
||||
private File pluginFolder;
|
||||
|
||||
private File internalDataFolder;
|
||||
|
||||
private DimensionRegistry dimensionRegistry;
|
||||
|
||||
private Metrics metrics;
|
||||
|
||||
public AtomicInteger entityIdCount = new AtomicInteger();
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
private Unsafe unsafe = new Unsafe();
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public Limbo() throws IOException, ParseException, NumberFormatException, ClassNotFoundException, InterruptedException {
|
||||
instance = this;
|
||||
|
||||
if (!noGui) {
|
||||
while (!GUI.loadFinish) {
|
||||
TimeUnit.MILLISECONDS.sleep(500);
|
||||
}
|
||||
console = new Console(null, System.out, System.err);
|
||||
} else {
|
||||
console = new Console(System.in, System.out, System.err);
|
||||
}
|
||||
|
||||
limboImplementationVersion = getLimboVersion();
|
||||
console.sendMessage("Loading Limbo Version " + limboImplementationVersion + " on Minecraft " + serverImplementationVersion);
|
||||
|
||||
String spName = "server.properties";
|
||||
File sp = new File(spName);
|
||||
if (!sp.exists()) {
|
||||
try (InputStream in = getClass().getClassLoader().getResourceAsStream(spName)) {
|
||||
Files.copy(in, sp.toPath());
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
properties = new ServerProperties(sp);
|
||||
|
||||
if (!properties.isBungeecord()) {
|
||||
console.sendMessage("If you are using bungeecord, consider turning that on in the settings!");
|
||||
} else {
|
||||
console.sendMessage("Starting Limbo server in bungeecord mode!");
|
||||
}
|
||||
|
||||
internalDataFolder = new File("internal_data");
|
||||
if (!internalDataFolder.exists()) {
|
||||
internalDataFolder.mkdirs();
|
||||
}
|
||||
|
||||
String mappingName = "mapping.json";
|
||||
File mappingFile = new File(internalDataFolder, mappingName);
|
||||
if (!mappingFile.exists()) {
|
||||
try (InputStream in = getClass().getClassLoader().getResourceAsStream(mappingName)) {
|
||||
Files.copy(in, mappingFile.toPath());
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
console.sendMessage("Loading packet id mappings from mapping.json ...");
|
||||
|
||||
JSONObject json = (JSONObject) new JSONParser().parse(new FileReader(mappingFile));
|
||||
|
||||
String classPrefix = Packet.class.getName().substring(0, Packet.class.getName().lastIndexOf(".") + 1);
|
||||
int mappingsCount = 0;
|
||||
|
||||
Map<Integer, Class<? extends PacketIn>> HandshakeIn = new HashMap<>();
|
||||
for (Object key : ((JSONObject) json.get("HandshakeIn")).keySet()) {
|
||||
int packetId = Integer.decode((String) key);
|
||||
HandshakeIn.put(packetId, (Class<? extends PacketIn>) Class.forName(classPrefix + (String) ((JSONObject) json.get("HandshakeIn")).get(key)));
|
||||
}
|
||||
Packet.setHandshakeIn(HandshakeIn);
|
||||
mappingsCount += HandshakeIn.size();
|
||||
|
||||
Map<Integer, Class<? extends PacketIn>> StatusIn = new HashMap<>();
|
||||
for (Object key : ((JSONObject) json.get("StatusIn")).keySet()) {
|
||||
int packetId = Integer.decode((String) key);
|
||||
StatusIn.put(packetId, (Class<? extends PacketIn>) Class.forName(classPrefix + (String) ((JSONObject) json.get("StatusIn")).get(key)));
|
||||
}
|
||||
Packet.setStatusIn(StatusIn);
|
||||
mappingsCount += StatusIn.size();
|
||||
|
||||
Map<Class<? extends PacketOut>, Integer> StatusOut = new HashMap<>();
|
||||
for (Object key : ((JSONObject) json.get("StatusOut")).keySet()) {
|
||||
Class<? extends PacketOut> packetClass = (Class<? extends PacketOut>) Class.forName(classPrefix + (String) key);
|
||||
StatusOut.put(packetClass, Integer.decode((String) ((JSONObject) json.get("StatusOut")).get(key)));
|
||||
}
|
||||
Packet.setStatusOut(StatusOut);
|
||||
mappingsCount += StatusOut.size();
|
||||
|
||||
Map<Integer, Class<? extends PacketIn>> LoginIn = new HashMap<>();
|
||||
for (Object key : ((JSONObject) json.get("LoginIn")).keySet()) {
|
||||
int packetId = Integer.decode((String) key);
|
||||
LoginIn.put(packetId, (Class<? extends PacketIn>) Class.forName(classPrefix + (String) ((JSONObject) json.get("LoginIn")).get(key)));
|
||||
}
|
||||
Packet.setLoginIn(LoginIn);
|
||||
mappingsCount += LoginIn.size();
|
||||
|
||||
Map<Class<? extends PacketOut>, Integer> LoginOut = new HashMap<>();
|
||||
for (Object key : ((JSONObject) json.get("LoginOut")).keySet()) {
|
||||
Class<? extends PacketOut> packetClass = (Class<? extends PacketOut>) Class.forName(classPrefix + (String) key);
|
||||
LoginOut.put(packetClass, Integer.decode((String) ((JSONObject) json.get("LoginOut")).get(key)));
|
||||
}
|
||||
Packet.setLoginOut(LoginOut);
|
||||
mappingsCount += LoginOut.size();
|
||||
|
||||
Map<Integer, Class<? extends PacketIn>> PlayIn = new HashMap<>();
|
||||
for (Object key : ((JSONObject) json.get("PlayIn")).keySet()) {
|
||||
int packetId = Integer.decode((String) key);
|
||||
PlayIn.put(packetId, (Class<? extends PacketIn>) Class.forName(classPrefix + (String) ((JSONObject) json.get("PlayIn")).get(key)));
|
||||
}
|
||||
Packet.setPlayIn(PlayIn);
|
||||
mappingsCount += PlayIn.size();
|
||||
|
||||
Map<Class<? extends PacketOut>, Integer> PlayOut = new HashMap<>();
|
||||
for (Object key : ((JSONObject) json.get("PlayOut")).keySet()) {
|
||||
Class<? extends PacketOut> packetClass = (Class<? extends PacketOut>) Class.forName(classPrefix + (String) key);
|
||||
PlayOut.put(packetClass, Integer.decode((String) ((JSONObject) json.get("PlayOut")).get(key)));
|
||||
}
|
||||
Packet.setPlayOut(PlayOut);
|
||||
mappingsCount += PlayOut.size();
|
||||
|
||||
console.sendMessage("Loaded all " + mappingsCount + " packet id mappings!");
|
||||
|
||||
dimensionRegistry = new DimensionRegistry();
|
||||
|
||||
worlds.add(loadDefaultWorld());
|
||||
Location spawn = properties.getWorldSpawn();
|
||||
properties.setWorldSpawn(new Location(getWorld(properties.getLevelName().getKey()), spawn.getX(), spawn.getY(), spawn.getZ(), spawn.getYaw(), spawn.getPitch()));
|
||||
|
||||
if (!NetworkUtils.available(properties.getServerPort())) {
|
||||
console.sendMessage("");
|
||||
console.sendMessage("*****FAILED TO BIND PORT [" + properties.getServerPort() + "]*****");
|
||||
console.sendMessage("*****PORT ALREADY IN USE*****");
|
||||
console.sendMessage("*****PERHAPS ANOTHER INSTANCE OF THE SERVER IS ALREADY RUNNING?*****");
|
||||
console.sendMessage("");
|
||||
System.exit(2);
|
||||
}
|
||||
|
||||
String permissionName = "permission.yml";
|
||||
File permissionFile = new File(permissionName);
|
||||
if (!permissionFile.exists()) {
|
||||
try (InputStream in = getClass().getClassLoader().getResourceAsStream(permissionName)) {
|
||||
Files.copy(in, permissionFile.toPath());
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
permissionManager = new PermissionsManager();
|
||||
permissionManager.loadDefaultPermissionFile(permissionFile);
|
||||
|
||||
eventsManager = new EventsManager();
|
||||
|
||||
pluginFolder = new File("plugins");
|
||||
pluginFolder.mkdirs();
|
||||
|
||||
File defaultCommandsJar = new File(pluginFolder, "LimboDefaultCmd.jar");
|
||||
defaultCommandsJar.delete();
|
||||
console.sendMessage("Downloading limbo default commands module from github...");
|
||||
ReadableByteChannel rbc = Channels.newChannel(new URL("https://github.com/LOOHP/Limbo/raw/master/modules/LimboDefaultCmd.jar").openStream());
|
||||
FileOutputStream fos = new FileOutputStream(defaultCommandsJar);
|
||||
fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE);
|
||||
fos.close();
|
||||
|
||||
pluginManager = new PluginManager(pluginFolder);
|
||||
try {
|
||||
Method loadPluginsMethod = PluginManager.class.getDeclaredMethod("loadPlugins");
|
||||
loadPluginsMethod.setAccessible(true);
|
||||
loadPluginsMethod.invoke(pluginManager);
|
||||
loadPluginsMethod.setAccessible(false);
|
||||
} catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
for (LimboPlugin plugin : Limbo.getInstance().getPluginManager().getPlugins()) {
|
||||
console.sendMessage("Enabling plugin " + plugin.getName() + " " + plugin.getInfo().getVersion());
|
||||
plugin.onEnable();
|
||||
}
|
||||
|
||||
server = new ServerConnection(properties.getServerIp(), properties.getServerPort());
|
||||
|
||||
metrics = new Metrics();
|
||||
|
||||
console.run();
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public Unsafe getUnsafe() {
|
||||
return unsafe;
|
||||
}
|
||||
|
||||
public DimensionRegistry getDimensionRegistry() {
|
||||
return dimensionRegistry;
|
||||
}
|
||||
|
||||
public PermissionsManager getPermissionsManager() {
|
||||
return permissionManager;
|
||||
}
|
||||
|
||||
public File getInternalDataFolder() {
|
||||
return internalDataFolder;
|
||||
}
|
||||
|
||||
public EventsManager getEventsManager() {
|
||||
return eventsManager;
|
||||
}
|
||||
|
||||
public File getPluginFolder() {
|
||||
return pluginFolder;
|
||||
}
|
||||
|
||||
public PluginManager getPluginManager() {
|
||||
return pluginManager;
|
||||
}
|
||||
|
||||
private World loadDefaultWorld() throws IOException {
|
||||
console.sendMessage("Loading world " + properties.getLevelName() + " with the schematic file " + properties.getSchemFileName() + " ...");
|
||||
|
||||
File schem = new File(properties.getSchemFileName());
|
||||
|
||||
if (!schem.exists()) {
|
||||
console.sendMessage("Schemetic file " + properties.getSchemFileName() + " for world " + properties.getLevelName() + " not found!");
|
||||
console.sendMessage("Server will exit!");
|
||||
System.exit(1);
|
||||
return null;
|
||||
}
|
||||
|
||||
World world = Schematic.toWorld(properties.getLevelName().getKey(), Environment.fromNamespacedKey(properties.getLevelDimension()), (CompoundTag) NBTUtil.read(schem).getTag());
|
||||
|
||||
console.sendMessage("Loaded world " + properties.getLevelName() + "!");
|
||||
|
||||
return world;
|
||||
}
|
||||
|
||||
public ServerProperties getServerProperties() {
|
||||
return properties;
|
||||
}
|
||||
|
||||
public ServerConnection getServerConnection() {
|
||||
return server;
|
||||
}
|
||||
|
||||
public Console getConsole() {
|
||||
return console;
|
||||
}
|
||||
|
||||
public Metrics getMetrics() {
|
||||
return metrics;
|
||||
}
|
||||
|
||||
public Set<Player> getPlayers() {
|
||||
return new HashSet<>(playersByUUID.values());
|
||||
}
|
||||
|
||||
public Player getPlayer(String name) {
|
||||
return playersByName.get(name);
|
||||
}
|
||||
|
||||
public Player getPlayer(UUID uuid) {
|
||||
return playersByUUID.get(uuid);
|
||||
}
|
||||
|
||||
public void addPlayer(Player player) {
|
||||
playersByName.put(player.getName(), player);
|
||||
playersByUUID.put(player.getUUID(), player);
|
||||
}
|
||||
|
||||
public void removePlayer(Player player) {
|
||||
playersByName.remove(player.getName());
|
||||
playersByUUID.remove(player.getUUID());
|
||||
}
|
||||
|
||||
public List<World> getWorlds() {
|
||||
return new ArrayList<>(worlds);
|
||||
}
|
||||
|
||||
public World getWorld(String name) {
|
||||
for (World world : worlds) {
|
||||
if (world.getName().equalsIgnoreCase(name)) {
|
||||
return world;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public String buildServerListResponseJson(String version, int protocol, BaseComponent[] motd, int maxPlayers, int playersOnline, BufferedImage favicon) throws IOException {
|
||||
JSONObject json = new JSONObject();
|
||||
|
||||
JSONObject versionJson = new JSONObject();
|
||||
versionJson.put("name", version);
|
||||
versionJson.put("protocol", protocol);
|
||||
json.put("version", versionJson);
|
||||
|
||||
JSONObject playersJson = new JSONObject();
|
||||
playersJson.put("max", maxPlayers);
|
||||
playersJson.put("online", playersOnline);
|
||||
json.put("players", playersJson);
|
||||
|
||||
json.put("description", "%MOTD%");
|
||||
|
||||
if (favicon != null) {
|
||||
if (favicon.getWidth() == 64 && favicon.getHeight() == 64) {
|
||||
String base64 = "data:image/png;base64," + ImageUtils.imgToBase64String(favicon, "png");
|
||||
json.put("favicon", base64);
|
||||
} else {
|
||||
console.sendMessage("Server List Favicon must be 64 x 64 in size!");
|
||||
}
|
||||
}
|
||||
|
||||
JSONObject modInfoJson = new JSONObject();
|
||||
modInfoJson.put("type", "FML");
|
||||
modInfoJson.put("modList", new JSONArray());
|
||||
json.put("modinfo", modInfoJson);
|
||||
|
||||
|
||||
TreeMap<String, Object> treeMap = new TreeMap<String, Object>(String.CASE_INSENSITIVE_ORDER);
|
||||
treeMap.putAll(json);
|
||||
|
||||
Gson g = new GsonBuilder().create();
|
||||
|
||||
return g.toJson(treeMap).replace("\"%MOTD%\"", ComponentSerializer.toString(motd));
|
||||
}
|
||||
|
||||
public String buildLegacyPingResponse(String version, BaseComponent[] motd, int maxPlayers, int playersOnline) {
|
||||
String begin = "§1";
|
||||
return String.join("\00", begin, "127", version, String.join("", Arrays.asList(motd).stream().map(each -> each.toLegacyText()).collect(Collectors.toList())), String.valueOf(playersOnline), String.valueOf(maxPlayers));
|
||||
}
|
||||
|
||||
public void stopServer() {
|
||||
console.sendMessage("Stopping Server...");
|
||||
|
||||
for (LimboPlugin plugin : Limbo.getInstance().getPluginManager().getPlugins()) {
|
||||
console.sendMessage("Disabling plugin " + plugin.getName() + " " + plugin.getInfo().getVersion());
|
||||
plugin.onDisable();
|
||||
}
|
||||
|
||||
for (Player player : getPlayers()) {
|
||||
player.disconnect("Server closed");
|
||||
}
|
||||
while (!getPlayers().isEmpty()) {
|
||||
try {
|
||||
TimeUnit.MILLISECONDS.sleep(500);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
console.sendMessage("Server closed");
|
||||
console.logs.close();
|
||||
System.exit(0);
|
||||
}
|
||||
|
||||
public int getNextEntityId() {
|
||||
if (entityIdCount.get() == Integer.MAX_VALUE) {
|
||||
return entityIdCount.getAndSet(0);
|
||||
} else {
|
||||
return entityIdCount.getAndIncrement();
|
||||
}
|
||||
}
|
||||
|
||||
public void dispatchCommand(CommandSender sender, String str) {
|
||||
String[] command;
|
||||
if (str.startsWith("/")) {
|
||||
command = CustomStringUtils.splitStringToArgs(str.substring(1));
|
||||
} else {
|
||||
command = CustomStringUtils.splitStringToArgs(str);
|
||||
}
|
||||
dispatchCommand(sender, command);
|
||||
}
|
||||
|
||||
public void dispatchCommand(CommandSender sender, String... args) {
|
||||
try {
|
||||
Limbo.getInstance().getPluginManager().fireExecutors(sender, args);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private String getLimboVersion() throws IOException {
|
||||
Enumeration<URL> manifests = getClass().getClassLoader().getResources("META-INF/MANIFEST.MF");
|
||||
while (manifests.hasMoreElements()) {
|
||||
URL url = manifests.nextElement();
|
||||
BufferedReader br = new BufferedReader(new InputStreamReader(url.openStream()));
|
||||
Optional<String> line = br.lines().filter(each -> each.startsWith("Limbo-Version:")).findFirst();
|
||||
br.close();
|
||||
if (line.isPresent()) {
|
||||
return line.get().substring(14).trim();
|
||||
}
|
||||
}
|
||||
return "Unknown";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,468 @@
|
||||
package com.loohp.limbo.Location;
|
||||
|
||||
import com.loohp.limbo.Limbo;
|
||||
import com.loohp.limbo.Utils.NumberConversions;
|
||||
import com.loohp.limbo.World.BlockState;
|
||||
import com.loohp.limbo.World.World;
|
||||
|
||||
public class Location implements Cloneable {
|
||||
|
||||
World world;
|
||||
double x;
|
||||
double y;
|
||||
double z;
|
||||
float yaw;
|
||||
float pitch;
|
||||
|
||||
public Location(World world, double x, double y, double z, float yaw, float pitch) {
|
||||
this.world = world;
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.z = z;
|
||||
this.yaw = yaw;
|
||||
this.pitch = pitch;
|
||||
}
|
||||
|
||||
public Location(World world, double x, double y, double z) {
|
||||
this(world, x, y, z, 0, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Location clone() {
|
||||
try {
|
||||
return (Location) super.clone();
|
||||
} catch (CloneNotSupportedException e) {
|
||||
throw new Error(e);
|
||||
}
|
||||
}
|
||||
|
||||
public BlockState getBlockState() {
|
||||
return world.getBlock((int) x,(int) y,(int) z);
|
||||
}
|
||||
|
||||
public void setBlockState(BlockState state) {
|
||||
world.setBlock((int) x, (int) y, (int) z, state);
|
||||
}
|
||||
|
||||
public boolean isWorldLoaded() {
|
||||
return Limbo.getInstance().getWorld(world.getName()) != null;
|
||||
}
|
||||
|
||||
public World getWorld() {
|
||||
return world;
|
||||
}
|
||||
|
||||
public void setWorld(World world) {
|
||||
this.world = world;
|
||||
}
|
||||
|
||||
public double getX() {
|
||||
return x;
|
||||
}
|
||||
|
||||
public void setX(double x) {
|
||||
this.x = x;
|
||||
}
|
||||
|
||||
public double getY() {
|
||||
return y;
|
||||
}
|
||||
|
||||
public void setY(double y) {
|
||||
this.y = y;
|
||||
}
|
||||
|
||||
public double getZ() {
|
||||
return z;
|
||||
}
|
||||
|
||||
public void setZ(double z) {
|
||||
this.z = z;
|
||||
}
|
||||
|
||||
public float getYaw() {
|
||||
return yaw;
|
||||
}
|
||||
|
||||
public void setYaw(float yaw) {
|
||||
this.yaw = yaw;
|
||||
}
|
||||
|
||||
public float getPitch() {
|
||||
return pitch;
|
||||
}
|
||||
|
||||
public void setPitch(float pitch) {
|
||||
this.pitch = pitch;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a unit-vector pointing in the direction that this Location is
|
||||
* facing.
|
||||
*
|
||||
* @return a vector pointing the direction of this location's {@link
|
||||
* #getPitch() pitch} and {@link #getYaw() yaw}
|
||||
*/
|
||||
public Vector getDirection() {
|
||||
Vector vector = new Vector();
|
||||
|
||||
double rotX = this.getYaw();
|
||||
double rotY = this.getPitch();
|
||||
|
||||
vector.setY(-Math.sin(Math.toRadians(rotY)));
|
||||
|
||||
double xz = Math.cos(Math.toRadians(rotY));
|
||||
|
||||
vector.setX(-xz * Math.sin(Math.toRadians(rotX)));
|
||||
vector.setZ(xz * Math.cos(Math.toRadians(rotX)));
|
||||
|
||||
return vector;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link #getYaw() yaw} and {@link #getPitch() pitch} to point
|
||||
* in the direction of the vector.
|
||||
*
|
||||
* @param vector the direction vector
|
||||
* @return the same location
|
||||
*/
|
||||
public Location setDirection(Vector vector) {
|
||||
/*
|
||||
* Sin = Opp / Hyp
|
||||
* Cos = Adj / Hyp
|
||||
* Tan = Opp / Adj
|
||||
*
|
||||
* x = -Opp
|
||||
* z = Adj
|
||||
*/
|
||||
final double _2PI = 2 * Math.PI;
|
||||
final double x = vector.getX();
|
||||
final double z = vector.getZ();
|
||||
|
||||
if (x == 0 && z == 0) {
|
||||
pitch = vector.getY() > 0 ? -90 : 90;
|
||||
return this;
|
||||
}
|
||||
|
||||
double theta = Math.atan2(-x, z);
|
||||
yaw = (float) Math.toDegrees((theta + _2PI) % _2PI);
|
||||
|
||||
double x2 = NumberConversions.square(x);
|
||||
double z2 = NumberConversions.square(z);
|
||||
double xz = Math.sqrt(x2 + z2);
|
||||
pitch = (float) Math.toDegrees(Math.atan(-vector.getY() / xz));
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the location by another.
|
||||
*
|
||||
* @see Vector
|
||||
* @param vec The other location
|
||||
* @return the same location
|
||||
* @throws IllegalArgumentException for differing worlds
|
||||
*/
|
||||
public Location add(Location vec) {
|
||||
if (vec == null || vec.getWorld() != getWorld()) {
|
||||
throw new IllegalArgumentException("Cannot add Locations of differing worlds");
|
||||
}
|
||||
|
||||
x += vec.x;
|
||||
y += vec.y;
|
||||
z += vec.z;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the location by a vector.
|
||||
*
|
||||
* @see Vector
|
||||
* @param vec Vector to use
|
||||
* @return the same location
|
||||
*/
|
||||
public Location add(Vector vec) {
|
||||
this.x += vec.getX();
|
||||
this.y += vec.getY();
|
||||
this.z += vec.getZ();
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the location by another. Not world-aware.
|
||||
*
|
||||
* @see Vector
|
||||
* @param x X coordinate
|
||||
* @param y Y coordinate
|
||||
* @param z Z coordinate
|
||||
* @return the same location
|
||||
*/
|
||||
public Location add(double x, double y, double z) {
|
||||
this.x += x;
|
||||
this.y += y;
|
||||
this.z += z;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Subtracts the location by another.
|
||||
*
|
||||
* @see Vector
|
||||
* @param vec The other location
|
||||
* @return the same location
|
||||
* @throws IllegalArgumentException for differing worlds
|
||||
*/
|
||||
public Location subtract(Location vec) {
|
||||
if (vec == null || vec.getWorld() != getWorld()) {
|
||||
throw new IllegalArgumentException("Cannot add Locations of differing worlds");
|
||||
}
|
||||
|
||||
x -= vec.x;
|
||||
y -= vec.y;
|
||||
z -= vec.z;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Subtracts the location by a vector.
|
||||
*
|
||||
* @see Vector
|
||||
* @param vec The vector to use
|
||||
* @return the same location
|
||||
*/
|
||||
public Location subtract(Vector vec) {
|
||||
this.x -= vec.getX();
|
||||
this.y -= vec.getY();
|
||||
this.z -= vec.getZ();
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Subtracts the location by another. Not world-aware and
|
||||
* orientation independent.
|
||||
*
|
||||
* @see Vector
|
||||
* @param x X coordinate
|
||||
* @param y Y coordinate
|
||||
* @param z Z coordinate
|
||||
* @return the same location
|
||||
*/
|
||||
public Location subtract(double x, double y, double z) {
|
||||
this.x -= x;
|
||||
this.y -= y;
|
||||
this.z -= z;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the magnitude of the location, defined as sqrt(x^2+y^2+z^2). The
|
||||
* value of this method is not cached and uses a costly square-root
|
||||
* function, so do not repeatedly call this method to get the location's
|
||||
* magnitude. NaN will be returned if the inner result of the sqrt()
|
||||
* function overflows, which will be caused if the length is too long. Not
|
||||
* world-aware and orientation independent.
|
||||
*
|
||||
* @return the magnitude
|
||||
* @see Vector
|
||||
*/
|
||||
public double length() {
|
||||
return Math.sqrt(NumberConversions.square(x) + NumberConversions.square(y) + NumberConversions.square(z));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the magnitude of the location squared. Not world-aware and
|
||||
* orientation independent.
|
||||
*
|
||||
* @return the magnitude
|
||||
* @see Vector
|
||||
*/
|
||||
public double lengthSquared() {
|
||||
return NumberConversions.square(x) + NumberConversions.square(y) + NumberConversions.square(z);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the distance between this location and another. The value of this
|
||||
* method is not cached and uses a costly square-root function, so do not
|
||||
* repeatedly call this method to get the location's magnitude. NaN will
|
||||
* be returned if the inner result of the sqrt() function overflows, which
|
||||
* will be caused if the distance is too long.
|
||||
*
|
||||
* @param o The other location
|
||||
* @return the distance
|
||||
* @throws IllegalArgumentException for differing worlds
|
||||
* @see Vector
|
||||
*/
|
||||
public double distance(Location o) {
|
||||
return Math.sqrt(distanceSquared(o));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the squared distance between this location and another.
|
||||
*
|
||||
* @param o The other location
|
||||
* @return the distance
|
||||
* @throws IllegalArgumentException for differing worlds
|
||||
* @see Vector
|
||||
*/
|
||||
public double distanceSquared(Location o) {
|
||||
if (o == null) {
|
||||
throw new IllegalArgumentException("Cannot measure distance to a null location");
|
||||
} else if (o.getWorld() == null || getWorld() == null) {
|
||||
throw new IllegalArgumentException("Cannot measure distance to a null world");
|
||||
} else if (o.getWorld() != getWorld()) {
|
||||
throw new IllegalArgumentException("Cannot measure distance between " + getWorld().getName() + " and " + o.getWorld().getName());
|
||||
}
|
||||
|
||||
return NumberConversions.square(x - o.x) + NumberConversions.square(y - o.y) + NumberConversions.square(z - o.z);
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs scalar multiplication, multiplying all components with a
|
||||
* scalar. Not world-aware.
|
||||
*
|
||||
* @param m The factor
|
||||
* @return the same location
|
||||
* @see Vector
|
||||
*/
|
||||
public Location multiply(double m) {
|
||||
x *= m;
|
||||
y *= m;
|
||||
z *= m;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Zero this location's components. Not world-aware.
|
||||
*
|
||||
* @return the same location
|
||||
* @see Vector
|
||||
*/
|
||||
public Location zero() {
|
||||
x = 0;
|
||||
y = 0;
|
||||
z = 0;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new {@link Vector} based on this Location
|
||||
*
|
||||
* @return New Vector containing the coordinates represented by this
|
||||
* Location
|
||||
*/
|
||||
public Vector toVector() {
|
||||
return new Vector(x, y, z);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if each component of this Location is finite.
|
||||
*
|
||||
* @throws IllegalArgumentException if any component is not finite
|
||||
*/
|
||||
public void checkFinite() throws IllegalArgumentException {
|
||||
NumberConversions.checkFinite(x, "x not finite");
|
||||
NumberConversions.checkFinite(y, "y not finite");
|
||||
NumberConversions.checkFinite(z, "z not finite");
|
||||
NumberConversions.checkFinite(pitch, "pitch not finite");
|
||||
NumberConversions.checkFinite(yaw, "yaw not finite");
|
||||
}
|
||||
|
||||
/**
|
||||
* Safely converts a double (location coordinate) to an int (block
|
||||
* coordinate)
|
||||
*
|
||||
* @param loc Precise coordinate
|
||||
* @return Block coordinate
|
||||
*/
|
||||
public static int locToBlock(double loc) {
|
||||
return NumberConversions.floor(loc);
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalizes the given yaw angle to a value between <code>+/-180</code>
|
||||
* degrees.
|
||||
*
|
||||
* @param yaw the yaw in degrees
|
||||
* @return the normalized yaw in degrees
|
||||
* @see Location#getYaw()
|
||||
*/
|
||||
public static float normalizeYaw(float yaw) {
|
||||
yaw %= 360.0f;
|
||||
if (yaw >= 180.0f) {
|
||||
yaw -= 360.0f;
|
||||
} else if (yaw < -180.0f) {
|
||||
yaw += 360.0f;
|
||||
}
|
||||
return yaw;
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalizes the given pitch angle to a value between <code>+/-90</code>
|
||||
* degrees.
|
||||
*
|
||||
* @param pitch the pitch in degrees
|
||||
* @return the normalized pitch in degrees
|
||||
* @see Location#getPitch()
|
||||
*/
|
||||
public static float normalizePitch(float pitch) {
|
||||
if (pitch > 90.0f) {
|
||||
pitch = 90.0f;
|
||||
} else if (pitch < -90.0f) {
|
||||
pitch = -90.0f;
|
||||
}
|
||||
return pitch;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Location{" + "world=" + world + ",x=" + x + ",y=" + y + ",z=" + z + ",pitch=" + pitch + ",yaw=" + yaw + "}";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
if (getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
final Location other = (Location) obj;
|
||||
|
||||
World world = (this.world == null) ? null : this.world;
|
||||
World otherWorld = (other.world == null) ? null : other.world;
|
||||
if (world != otherWorld && (world == null || !world.equals(otherWorld))) {
|
||||
return false;
|
||||
}
|
||||
if (Double.doubleToLongBits(this.x) != Double.doubleToLongBits(other.x)) {
|
||||
return false;
|
||||
}
|
||||
if (Double.doubleToLongBits(this.y) != Double.doubleToLongBits(other.y)) {
|
||||
return false;
|
||||
}
|
||||
if (Double.doubleToLongBits(this.z) != Double.doubleToLongBits(other.z)) {
|
||||
return false;
|
||||
}
|
||||
if (Float.floatToIntBits(this.pitch) != Float.floatToIntBits(other.pitch)) {
|
||||
return false;
|
||||
}
|
||||
if (Float.floatToIntBits(this.yaw) != Float.floatToIntBits(other.yaw)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int hash = 3;
|
||||
|
||||
World world = (this.world == null) ? null : this.world;
|
||||
hash = 19 * hash + (world != null ? world.hashCode() : 0);
|
||||
hash = 19 * hash + (int) (Double.doubleToLongBits(this.x) ^ (Double.doubleToLongBits(this.x) >>> 32));
|
||||
hash = 19 * hash + (int) (Double.doubleToLongBits(this.y) ^ (Double.doubleToLongBits(this.y) >>> 32));
|
||||
hash = 19 * hash + (int) (Double.doubleToLongBits(this.z) ^ (Double.doubleToLongBits(this.z) >>> 32));
|
||||
hash = 19 * hash + Float.floatToIntBits(this.pitch);
|
||||
hash = 19 * hash + Float.floatToIntBits(this.yaw);
|
||||
return hash;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,852 @@
|
||||
package com.loohp.limbo.Location;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.primitives.Doubles;
|
||||
import com.loohp.limbo.Utils.NumberConversions;
|
||||
import com.loohp.limbo.World.World;
|
||||
|
||||
/**
|
||||
* Represents a mutable vector. Because the components of Vectors are mutable,
|
||||
* storing Vectors long term may be dangerous if passing code modifies the
|
||||
* Vector later. If you want to keep around a Vector, it may be wise to call
|
||||
* <code>clone()</code> in order to get a copy.
|
||||
*/
|
||||
public class Vector implements Cloneable {
|
||||
|
||||
private static Random random = new Random();
|
||||
|
||||
/**
|
||||
* Threshold for fuzzy equals().
|
||||
*/
|
||||
private static final double epsilon = 0.000001;
|
||||
|
||||
protected double x;
|
||||
protected double y;
|
||||
protected double z;
|
||||
|
||||
/**
|
||||
* Construct the vector with all components as 0.
|
||||
*/
|
||||
public Vector() {
|
||||
this.x = 0;
|
||||
this.y = 0;
|
||||
this.z = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct the vector with provided integer components.
|
||||
*
|
||||
* @param x X component
|
||||
* @param y Y component
|
||||
* @param z Z component
|
||||
*/
|
||||
public Vector(int x, int y, int z) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.z = z;
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct the vector with provided double components.
|
||||
*
|
||||
* @param x X component
|
||||
* @param y Y component
|
||||
* @param z Z component
|
||||
*/
|
||||
public Vector(double x, double y, double z) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.z = z;
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct the vector with provided float components.
|
||||
*
|
||||
* @param x X component
|
||||
* @param y Y component
|
||||
* @param z Z component
|
||||
*/
|
||||
public Vector(float x, float y, float z) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.z = z;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a vector to this one
|
||||
*
|
||||
* @param vec The other vector
|
||||
* @return the same vector
|
||||
*/
|
||||
public Vector add(Vector vec) {
|
||||
x += vec.x;
|
||||
y += vec.y;
|
||||
z += vec.z;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Subtracts a vector from this one.
|
||||
*
|
||||
* @param vec The other vector
|
||||
* @return the same vector
|
||||
*/
|
||||
public Vector subtract(Vector vec) {
|
||||
x -= vec.x;
|
||||
y -= vec.y;
|
||||
z -= vec.z;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Multiplies the vector by another.
|
||||
*
|
||||
* @param vec The other vector
|
||||
* @return the same vector
|
||||
*/
|
||||
public Vector multiply(Vector vec) {
|
||||
x *= vec.x;
|
||||
y *= vec.y;
|
||||
z *= vec.z;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Divides the vector by another.
|
||||
*
|
||||
* @param vec The other vector
|
||||
* @return the same vector
|
||||
*/
|
||||
public Vector divide(Vector vec) {
|
||||
x /= vec.x;
|
||||
y /= vec.y;
|
||||
z /= vec.z;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies another vector
|
||||
*
|
||||
* @param vec The other vector
|
||||
* @return the same vector
|
||||
*/
|
||||
public Vector copy(Vector vec) {
|
||||
x = vec.x;
|
||||
y = vec.y;
|
||||
z = vec.z;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the magnitude of the vector, defined as sqrt(x^2+y^2+z^2). The
|
||||
* value of this method is not cached and uses a costly square-root
|
||||
* function, so do not repeatedly call this method to get the vector's
|
||||
* magnitude. NaN will be returned if the inner result of the sqrt()
|
||||
* function overflows, which will be caused if the length is too long.
|
||||
*
|
||||
* @return the magnitude
|
||||
*/
|
||||
public double length() {
|
||||
return Math.sqrt(NumberConversions.square(x) + NumberConversions.square(y) + NumberConversions.square(z));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the magnitude of the vector squared.
|
||||
*
|
||||
* @return the magnitude
|
||||
*/
|
||||
public double lengthSquared() {
|
||||
return NumberConversions.square(x) + NumberConversions.square(y) + NumberConversions.square(z);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the distance between this vector and another. The value of this
|
||||
* method is not cached and uses a costly square-root function, so do not
|
||||
* repeatedly call this method to get the vector's magnitude. NaN will be
|
||||
* returned if the inner result of the sqrt() function overflows, which
|
||||
* will be caused if the distance is too long.
|
||||
*
|
||||
* @param o The other vector
|
||||
* @return the distance
|
||||
*/
|
||||
public double distance(Vector o) {
|
||||
return Math.sqrt(NumberConversions.square(x - o.x) + NumberConversions.square(y - o.y) + NumberConversions.square(z - o.z));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the squared distance between this vector and another.
|
||||
*
|
||||
* @param o The other vector
|
||||
* @return the distance
|
||||
*/
|
||||
public double distanceSquared(Vector o) {
|
||||
return NumberConversions.square(x - o.x) + NumberConversions.square(y - o.y) + NumberConversions.square(z - o.z);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the angle between this vector and another in radians.
|
||||
*
|
||||
* @param other The other vector
|
||||
* @return angle in radians
|
||||
*/
|
||||
public float angle(Vector other) {
|
||||
double dot = Doubles.constrainToRange(dot(other) / (length() * other.length()), -1.0, 1.0);
|
||||
|
||||
return (float) Math.acos(dot);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets this vector to the midpoint between this vector and another.
|
||||
*
|
||||
* @param other The other vector
|
||||
* @return this same vector (now a midpoint)
|
||||
*/
|
||||
public Vector midpoint(Vector other) {
|
||||
x = (x + other.x) / 2;
|
||||
y = (y + other.y) / 2;
|
||||
z = (z + other.z) / 2;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a new midpoint vector between this vector and another.
|
||||
*
|
||||
* @param other The other vector
|
||||
* @return a new midpoint vector
|
||||
*/
|
||||
public Vector getMidpoint(Vector other) {
|
||||
double x = (this.x + other.x) / 2;
|
||||
double y = (this.y + other.y) / 2;
|
||||
double z = (this.z + other.z) / 2;
|
||||
return new Vector(x, y, z);
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs scalar multiplication, multiplying all components with a
|
||||
* scalar.
|
||||
*
|
||||
* @param m The factor
|
||||
* @return the same vector
|
||||
*/
|
||||
public Vector multiply(int m) {
|
||||
x *= m;
|
||||
y *= m;
|
||||
z *= m;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs scalar multiplication, multiplying all components with a
|
||||
* scalar.
|
||||
*
|
||||
* @param m The factor
|
||||
* @return the same vector
|
||||
*/
|
||||
public Vector multiply(double m) {
|
||||
x *= m;
|
||||
y *= m;
|
||||
z *= m;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs scalar multiplication, multiplying all components with a
|
||||
* scalar.
|
||||
*
|
||||
* @param m The factor
|
||||
* @return the same vector
|
||||
*/
|
||||
public Vector multiply(float m) {
|
||||
x *= m;
|
||||
y *= m;
|
||||
z *= m;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the dot product of this vector with another. The dot product
|
||||
* is defined as x1*x2+y1*y2+z1*z2. The returned value is a scalar.
|
||||
*
|
||||
* @param other The other vector
|
||||
* @return dot product
|
||||
*/
|
||||
public double dot(Vector other) {
|
||||
return x * other.x + y * other.y + z * other.z;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the cross product of this vector with another. The cross
|
||||
* product is defined as:
|
||||
* <ul>
|
||||
* <li>x = y1 * z2 - y2 * z1
|
||||
* <li>y = z1 * x2 - z2 * x1
|
||||
* <li>z = x1 * y2 - x2 * y1
|
||||
* </ul>
|
||||
*
|
||||
* @param o The other vector
|
||||
* @return the same vector
|
||||
*/
|
||||
public Vector crossProduct(Vector o) {
|
||||
double newX = y * o.z - o.y * z;
|
||||
double newY = z * o.x - o.z * x;
|
||||
double newZ = x * o.y - o.x * y;
|
||||
|
||||
x = newX;
|
||||
y = newY;
|
||||
z = newZ;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the cross product of this vector with another without mutating
|
||||
* the original. The cross product is defined as:
|
||||
* <ul>
|
||||
* <li>x = y1 * z2 - y2 * z1
|
||||
* <li>y = z1 * x2 - z2 * x1
|
||||
* <li>z = x1 * y2 - x2 * y1
|
||||
* </ul>
|
||||
*
|
||||
* @param o The other vector
|
||||
* @return a new vector
|
||||
*/
|
||||
public Vector getCrossProduct(Vector o) {
|
||||
double x = this.y * o.z - o.y * this.z;
|
||||
double y = this.z * o.x - o.z * this.x;
|
||||
double z = this.x * o.y - o.x * this.y;
|
||||
return new Vector(x, y, z);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts this vector to a unit vector (a vector with length of 1).
|
||||
*
|
||||
* @return the same vector
|
||||
*/
|
||||
public Vector normalize() {
|
||||
double length = length();
|
||||
|
||||
x /= length;
|
||||
y /= length;
|
||||
z /= length;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Zero this vector's components.
|
||||
*
|
||||
* @return the same vector
|
||||
*/
|
||||
public Vector zero() {
|
||||
x = 0;
|
||||
y = 0;
|
||||
z = 0;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts each component of value <code>-0.0</code> to <code>0.0</code>.
|
||||
*
|
||||
* @return This vector.
|
||||
*/
|
||||
Vector normalizeZeros() {
|
||||
if (x == -0.0D) x = 0.0D;
|
||||
if (y == -0.0D) y = 0.0D;
|
||||
if (z == -0.0D) z = 0.0D;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether this vector is in an axis-aligned bounding box.
|
||||
* <p>
|
||||
* The minimum and maximum vectors given must be truly the minimum and
|
||||
* maximum X, Y and Z components.
|
||||
*
|
||||
* @param min Minimum vector
|
||||
* @param max Maximum vector
|
||||
* @return whether this vector is in the AABB
|
||||
*/
|
||||
public boolean isInAABB(Vector min, Vector max) {
|
||||
return x >= min.x && x <= max.x && y >= min.y && y <= max.y && z >= min.z && z <= max.z;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether this vector is within a sphere.
|
||||
*
|
||||
* @param origin Sphere origin.
|
||||
* @param radius Sphere radius
|
||||
* @return whether this vector is in the sphere
|
||||
*/
|
||||
public boolean isInSphere(Vector origin, double radius) {
|
||||
return (NumberConversions.square(origin.x - x) + NumberConversions.square(origin.y - y) + NumberConversions.square(origin.z - z)) <= NumberConversions.square(radius);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns if a vector is normalized
|
||||
*
|
||||
* @return whether the vector is normalised
|
||||
*/
|
||||
public boolean isNormalized() {
|
||||
return Math.abs(this.lengthSquared() - 1) < getEpsilon();
|
||||
}
|
||||
|
||||
/**
|
||||
* Rotates the vector around the x axis.
|
||||
* <p>
|
||||
* This piece of math is based on the standard rotation matrix for vectors
|
||||
* in three dimensional space. This matrix can be found here:
|
||||
* <a href="https://en.wikipedia.org/wiki/Rotation_matrix#Basic_rotations">Rotation
|
||||
* Matrix</a>.
|
||||
*
|
||||
* @param angle the angle to rotate the vector about. This angle is passed
|
||||
* in radians
|
||||
* @return the same vector
|
||||
*/
|
||||
public Vector rotateAroundX(double angle) {
|
||||
double angleCos = Math.cos(angle);
|
||||
double angleSin = Math.sin(angle);
|
||||
|
||||
double y = angleCos * getY() - angleSin * getZ();
|
||||
double z = angleSin * getY() + angleCos * getZ();
|
||||
return setY(y).setZ(z);
|
||||
}
|
||||
|
||||
/**
|
||||
* Rotates the vector around the y axis.
|
||||
* <p>
|
||||
* This piece of math is based on the standard rotation matrix for vectors
|
||||
* in three dimensional space. This matrix can be found here:
|
||||
* <a href="https://en.wikipedia.org/wiki/Rotation_matrix#Basic_rotations">Rotation
|
||||
* Matrix</a>.
|
||||
*
|
||||
* @param angle the angle to rotate the vector about. This angle is passed
|
||||
* in radians
|
||||
* @return the same vector
|
||||
*/
|
||||
public Vector rotateAroundY(double angle) {
|
||||
double angleCos = Math.cos(angle);
|
||||
double angleSin = Math.sin(angle);
|
||||
|
||||
double x = angleCos * getX() + angleSin * getZ();
|
||||
double z = -angleSin * getX() + angleCos * getZ();
|
||||
return setX(x).setZ(z);
|
||||
}
|
||||
|
||||
/**
|
||||
* Rotates the vector around the z axis
|
||||
* <p>
|
||||
* This piece of math is based on the standard rotation matrix for vectors
|
||||
* in three dimensional space. This matrix can be found here:
|
||||
* <a href="https://en.wikipedia.org/wiki/Rotation_matrix#Basic_rotations">Rotation
|
||||
* Matrix</a>.
|
||||
*
|
||||
* @param angle the angle to rotate the vector about. This angle is passed
|
||||
* in radians
|
||||
* @return the same vector
|
||||
*/
|
||||
public Vector rotateAroundZ(double angle) {
|
||||
double angleCos = Math.cos(angle);
|
||||
double angleSin = Math.sin(angle);
|
||||
|
||||
double x = angleCos * getX() - angleSin * getY();
|
||||
double y = angleSin * getX() + angleCos * getY();
|
||||
return setX(x).setY(y);
|
||||
}
|
||||
|
||||
/**
|
||||
* Rotates the vector around a given arbitrary axis in 3 dimensional space.
|
||||
*
|
||||
* <p>
|
||||
* Rotation will follow the general Right-Hand-Rule, which means rotation
|
||||
* will be counterclockwise when the axis is pointing towards the observer.
|
||||
* <p>
|
||||
* This method will always make sure the provided axis is a unit vector, to
|
||||
* not modify the length of the vector when rotating. If you are experienced
|
||||
* with the scaling of a non-unit axis vector, you can use
|
||||
* {@link Vector#rotateAroundNonUnitAxis(Vector, double)}.
|
||||
*
|
||||
* @param axis the axis to rotate the vector around. If the passed vector is
|
||||
* not of length 1, it gets copied and normalized before using it for the
|
||||
* rotation. Please use {@link Vector#normalize()} on the instance before
|
||||
* passing it to this method
|
||||
* @param angle the angle to rotate the vector around the axis
|
||||
* @return the same vector
|
||||
* @throws IllegalArgumentException if the provided axis vector instance is
|
||||
* null
|
||||
*/
|
||||
public Vector rotateAroundAxis(Vector axis, double angle) throws IllegalArgumentException {
|
||||
Preconditions.checkArgument(axis != null, "The provided axis vector was null");
|
||||
|
||||
return rotateAroundNonUnitAxis(axis.isNormalized() ? axis : axis.clone().normalize(), angle);
|
||||
}
|
||||
|
||||
/**
|
||||
* Rotates the vector around a given arbitrary axis in 3 dimensional space.
|
||||
*
|
||||
* <p>
|
||||
* Rotation will follow the general Right-Hand-Rule, which means rotation
|
||||
* will be counterclockwise when the axis is pointing towards the observer.
|
||||
* <p>
|
||||
* Note that the vector length will change accordingly to the axis vector
|
||||
* length. If the provided axis is not a unit vector, the rotated vector
|
||||
* will not have its previous length. The scaled length of the resulting
|
||||
* vector will be related to the axis vector. If you are not perfectly sure
|
||||
* about the scaling of the vector, use
|
||||
* {@link Vector#rotateAroundAxis(Vector, double)}
|
||||
*
|
||||
* @param axis the axis to rotate the vector around.
|
||||
* @param angle the angle to rotate the vector around the axis
|
||||
* @return the same vector
|
||||
* @throws IllegalArgumentException if the provided axis vector instance is
|
||||
* null
|
||||
*/
|
||||
public Vector rotateAroundNonUnitAxis(Vector axis, double angle) throws IllegalArgumentException {
|
||||
Preconditions.checkArgument(axis != null, "The provided axis vector was null");
|
||||
|
||||
double x = getX(), y = getY(), z = getZ();
|
||||
double x2 = axis.getX(), y2 = axis.getY(), z2 = axis.getZ();
|
||||
|
||||
double cosTheta = Math.cos(angle);
|
||||
double sinTheta = Math.sin(angle);
|
||||
double dotProduct = this.dot(axis);
|
||||
|
||||
double xPrime = x2 * dotProduct * (1d - cosTheta)
|
||||
+ x * cosTheta
|
||||
+ (-z2 * y + y2 * z) * sinTheta;
|
||||
double yPrime = y2 * dotProduct * (1d - cosTheta)
|
||||
+ y * cosTheta
|
||||
+ (z2 * x - x2 * z) * sinTheta;
|
||||
double zPrime = z2 * dotProduct * (1d - cosTheta)
|
||||
+ z * cosTheta
|
||||
+ (-y2 * x + x2 * y) * sinTheta;
|
||||
|
||||
return setX(xPrime).setY(yPrime).setZ(zPrime);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the X component.
|
||||
*
|
||||
* @return The X component.
|
||||
*/
|
||||
public double getX() {
|
||||
return x;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the floored value of the X component, indicating the block that
|
||||
* this vector is contained with.
|
||||
*
|
||||
* @return block X
|
||||
*/
|
||||
public int getBlockX() {
|
||||
return NumberConversions.floor(x);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the Y component.
|
||||
*
|
||||
* @return The Y component.
|
||||
*/
|
||||
public double getY() {
|
||||
return y;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the floored value of the Y component, indicating the block that
|
||||
* this vector is contained with.
|
||||
*
|
||||
* @return block y
|
||||
*/
|
||||
public int getBlockY() {
|
||||
return NumberConversions.floor(y);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the Z component.
|
||||
*
|
||||
* @return The Z component.
|
||||
*/
|
||||
public double getZ() {
|
||||
return z;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the floored value of the Z component, indicating the block that
|
||||
* this vector is contained with.
|
||||
*
|
||||
* @return block z
|
||||
*/
|
||||
public int getBlockZ() {
|
||||
return NumberConversions.floor(z);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the X component.
|
||||
*
|
||||
* @param x The new X component.
|
||||
* @return This vector.
|
||||
*/
|
||||
public Vector setX(int x) {
|
||||
this.x = x;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the X component.
|
||||
*
|
||||
* @param x The new X component.
|
||||
* @return This vector.
|
||||
*/
|
||||
public Vector setX(double x) {
|
||||
this.x = x;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the X component.
|
||||
*
|
||||
* @param x The new X component.
|
||||
* @return This vector.
|
||||
*/
|
||||
public Vector setX(float x) {
|
||||
this.x = x;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the Y component.
|
||||
*
|
||||
* @param y The new Y component.
|
||||
* @return This vector.
|
||||
*/
|
||||
public Vector setY(int y) {
|
||||
this.y = y;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the Y component.
|
||||
*
|
||||
* @param y The new Y component.
|
||||
* @return This vector.
|
||||
*/
|
||||
public Vector setY(double y) {
|
||||
this.y = y;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the Y component.
|
||||
*
|
||||
* @param y The new Y component.
|
||||
* @return This vector.
|
||||
*/
|
||||
public Vector setY(float y) {
|
||||
this.y = y;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the Z component.
|
||||
*
|
||||
* @param z The new Z component.
|
||||
* @return This vector.
|
||||
*/
|
||||
public Vector setZ(int z) {
|
||||
this.z = z;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the Z component.
|
||||
*
|
||||
* @param z The new Z component.
|
||||
* @return This vector.
|
||||
*/
|
||||
public Vector setZ(double z) {
|
||||
this.z = z;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the Z component.
|
||||
*
|
||||
* @param z The new Z component.
|
||||
* @return This vector.
|
||||
*/
|
||||
public Vector setZ(float z) {
|
||||
this.z = z;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks to see if two objects are equal.
|
||||
* <p>
|
||||
* Only two Vectors can ever return true. This method uses a fuzzy match
|
||||
* to account for floating point errors. The epsilon can be retrieved
|
||||
* with epsilon.
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (!(obj instanceof Vector)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Vector other = (Vector) obj;
|
||||
|
||||
return Math.abs(x - other.x) < epsilon && Math.abs(y - other.y) < epsilon && Math.abs(z - other.z) < epsilon && (this.getClass().equals(obj.getClass()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a hash code for this vector
|
||||
*
|
||||
* @return hash code
|
||||
*/
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int hash = 7;
|
||||
|
||||
hash = 79 * hash + (int) (Double.doubleToLongBits(this.x) ^ (Double.doubleToLongBits(this.x) >>> 32));
|
||||
hash = 79 * hash + (int) (Double.doubleToLongBits(this.y) ^ (Double.doubleToLongBits(this.y) >>> 32));
|
||||
hash = 79 * hash + (int) (Double.doubleToLongBits(this.z) ^ (Double.doubleToLongBits(this.z) >>> 32));
|
||||
return hash;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a new vector.
|
||||
*
|
||||
* @return vector
|
||||
*/
|
||||
@Override
|
||||
public Vector clone() {
|
||||
try {
|
||||
return (Vector) super.clone();
|
||||
} catch (CloneNotSupportedException e) {
|
||||
throw new Error(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns this vector's components as x,y,z.
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return x + "," + y + "," + z;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a Location version of this vector with yaw and pitch being 0.
|
||||
*
|
||||
* @param world The world to link the location to.
|
||||
* @return the location
|
||||
*/
|
||||
public Location toLocation(World world) {
|
||||
return new Location(world, x, y, z);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a Location version of this vector.
|
||||
*
|
||||
* @param world The world to link the location to.
|
||||
* @param yaw The desired yaw.
|
||||
* @param pitch The desired pitch.
|
||||
* @return the location
|
||||
*/
|
||||
public Location toLocation(World world, float yaw, float pitch) {
|
||||
return new Location(world, x, y, z, yaw, pitch);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the block vector of this vector.
|
||||
*
|
||||
* @return A block vector.
|
||||
public BlockVector toBlockVector() {
|
||||
return new BlockVector(x, y, z);
|
||||
}
|
||||
*/
|
||||
|
||||
/**
|
||||
* Check if each component of this Vector is finite.
|
||||
*
|
||||
* @throws IllegalArgumentException if any component is not finite
|
||||
*/
|
||||
public void checkFinite() throws IllegalArgumentException {
|
||||
NumberConversions.checkFinite(x, "x not finite");
|
||||
NumberConversions.checkFinite(y, "y not finite");
|
||||
NumberConversions.checkFinite(z, "z not finite");
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the threshold used for equals().
|
||||
*
|
||||
* @return The epsilon.
|
||||
*/
|
||||
public static double getEpsilon() {
|
||||
return epsilon;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the minimum components of two vectors.
|
||||
*
|
||||
* @param v1 The first vector.
|
||||
* @param v2 The second vector.
|
||||
* @return minimum
|
||||
*/
|
||||
public static Vector getMinimum(Vector v1, Vector v2) {
|
||||
return new Vector(Math.min(v1.x, v2.x), Math.min(v1.y, v2.y), Math.min(v1.z, v2.z));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the maximum components of two vectors.
|
||||
*
|
||||
* @param v1 The first vector.
|
||||
* @param v2 The second vector.
|
||||
* @return maximum
|
||||
*/
|
||||
public static Vector getMaximum(Vector v1, Vector v2) {
|
||||
return new Vector(Math.max(v1.x, v2.x), Math.max(v1.y, v2.y), Math.max(v1.z, v2.z));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a random vector with components having a random value between 0
|
||||
* and 1.
|
||||
*
|
||||
* @return A random vector.
|
||||
*/
|
||||
public static Vector getRandom() {
|
||||
return new Vector(random.nextDouble(), random.nextDouble(), random.nextDouble());
|
||||
}
|
||||
|
||||
/*
|
||||
@Override
|
||||
public Map<String, Object> serialize() {
|
||||
Map<String, Object> result = new LinkedHashMap<String, Object>();
|
||||
|
||||
result.put("x", getX());
|
||||
result.put("y", getY());
|
||||
result.put("z", getZ());
|
||||
|
||||
return result;
|
||||
}
|
||||
*/
|
||||
/*
|
||||
public static Vector deserialize(Map<String, Object> args) {
|
||||
double x = 0;
|
||||
double y = 0;
|
||||
double z = 0;
|
||||
|
||||
if (args.containsKey("x")) {
|
||||
x = (Double) args.get("x");
|
||||
}
|
||||
if (args.containsKey("y")) {
|
||||
y = (Double) args.get("y");
|
||||
}
|
||||
if (args.containsKey("z")) {
|
||||
z = (Double) args.get("z");
|
||||
}
|
||||
|
||||
return new Vector(x, y, z);
|
||||
}
|
||||
*/
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,69 @@
|
||||
package com.loohp.limbo.Permissions;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.loohp.limbo.Console;
|
||||
import com.loohp.limbo.Commands.CommandSender;
|
||||
import com.loohp.limbo.File.FileConfiguration;
|
||||
import com.loohp.limbo.Player.Player;
|
||||
|
||||
public class PermissionsManager {
|
||||
|
||||
private Map<String, List<String>> users;
|
||||
private Map<String, List<String>> permissions;
|
||||
|
||||
public PermissionsManager() {
|
||||
users = new HashMap<>();
|
||||
permissions = new HashMap<>();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void loadDefaultPermissionFile(File file) throws FileNotFoundException {
|
||||
FileConfiguration config = new FileConfiguration(file);
|
||||
permissions.put("default", new ArrayList<>());
|
||||
try {
|
||||
for (Object obj : config.get("groups", Map.class).keySet()) {
|
||||
String key = (String) obj;
|
||||
List<String> nodes = new ArrayList<>();
|
||||
nodes.addAll(config.get("groups." + key, List.class));
|
||||
permissions.put(key, nodes);
|
||||
}
|
||||
} catch (Exception e) {}
|
||||
try {
|
||||
for (Object obj : config.get("players", Map.class).keySet()) {
|
||||
String key = (String) obj;
|
||||
List<String> groups = new ArrayList<>();
|
||||
groups.addAll(config.get("players." + key, List.class));
|
||||
users.put(key, groups);
|
||||
}
|
||||
} catch (Exception e) {}
|
||||
}
|
||||
|
||||
public boolean hasPermission(CommandSender sender, String permission) {
|
||||
if (sender instanceof Console) {
|
||||
return true;
|
||||
} else if (sender instanceof Player) {
|
||||
Player player = (Player) sender;
|
||||
if (users.get(player.getName()) != null && users.get(player.getName()).stream().anyMatch(each -> permissions.get(each).stream().anyMatch(node -> node.equalsIgnoreCase(permission)))) {
|
||||
return true;
|
||||
} else {
|
||||
return permissions.get("default").stream().anyMatch(node -> node.equalsIgnoreCase(permission));
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public Map<String, List<String>> getUsers() {
|
||||
return users;
|
||||
}
|
||||
|
||||
public Map<String, List<String>> getPermissions() {
|
||||
return permissions;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,168 @@
|
||||
package com.loohp.limbo.Player;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.UUID;
|
||||
|
||||
import com.loohp.limbo.Limbo;
|
||||
import com.loohp.limbo.Commands.CommandSender;
|
||||
import com.loohp.limbo.Events.PlayerChatEvent;
|
||||
import com.loohp.limbo.Events.PlayerTeleportEvent;
|
||||
import com.loohp.limbo.Location.Location;
|
||||
import com.loohp.limbo.Server.ClientConnection;
|
||||
import com.loohp.limbo.Server.Packets.PacketPlayOutChat;
|
||||
import com.loohp.limbo.Server.Packets.PacketPlayOutGameState;
|
||||
import com.loohp.limbo.Server.Packets.PacketPlayOutPositionAndLook;
|
||||
import com.loohp.limbo.Server.Packets.PacketPlayOutRespawn;
|
||||
import com.loohp.limbo.Utils.GameMode;
|
||||
import com.loohp.limbo.World.World;
|
||||
|
||||
import net.md_5.bungee.api.chat.BaseComponent;
|
||||
import net.md_5.bungee.api.chat.TextComponent;
|
||||
import net.md_5.bungee.chat.ComponentSerializer;
|
||||
|
||||
public class Player implements CommandSender {
|
||||
|
||||
public final ClientConnection clientConnection;
|
||||
|
||||
private final String username;
|
||||
private final UUID uuid;
|
||||
protected GameMode gamemode;
|
||||
|
||||
protected int entityId;
|
||||
|
||||
private Location location;
|
||||
|
||||
public Player(ClientConnection clientConnection, String username, UUID uuid, int entityId, Location location) {
|
||||
this.clientConnection = clientConnection;
|
||||
this.username = username;
|
||||
this.uuid = uuid;
|
||||
this.entityId = entityId;
|
||||
this.location = location.clone();
|
||||
}
|
||||
|
||||
public GameMode getGamemode() {
|
||||
return gamemode;
|
||||
}
|
||||
|
||||
public void setGamemode(GameMode gamemode) {
|
||||
if (!this.gamemode.equals(gamemode)) {
|
||||
try {
|
||||
PacketPlayOutGameState state = new PacketPlayOutGameState(3, gamemode.getId());
|
||||
clientConnection.sendPacket(state);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
this.gamemode = gamemode;
|
||||
}
|
||||
|
||||
public World getWorld() {
|
||||
return location.clone().getWorld();
|
||||
}
|
||||
|
||||
public int getEntityId() {
|
||||
return entityId;
|
||||
}
|
||||
|
||||
public Location getLocation() {
|
||||
return location.clone();
|
||||
}
|
||||
|
||||
public void setLocation(Location location) {
|
||||
this.location = location;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return username;
|
||||
}
|
||||
|
||||
public UUID getUUID() {
|
||||
return uuid;
|
||||
}
|
||||
|
||||
public boolean hasPermission(String permission) {
|
||||
return Limbo.getInstance().getPermissionsManager().hasPermission(this, permission);
|
||||
}
|
||||
|
||||
public void teleport(Location location) {
|
||||
PlayerTeleportEvent event = Limbo.getInstance().getEventsManager().callEvent(new PlayerTeleportEvent(this, getLocation(), location));
|
||||
if (!event.isCancelled()) {
|
||||
location = event.getTo();
|
||||
try {
|
||||
if (!this.location.getWorld().equals(location.getWorld())) {
|
||||
PacketPlayOutRespawn respawn = new PacketPlayOutRespawn(location.getWorld(), Limbo.getInstance().getDimensionRegistry().getCodec(), 0, gamemode, false, false, true);
|
||||
clientConnection.sendPacket(respawn);
|
||||
}
|
||||
PacketPlayOutPositionAndLook positionLook = new PacketPlayOutPositionAndLook(location.getX(), location.getY(), location.getZ(), location.getYaw(), location.getPitch(), 1);
|
||||
clientConnection.sendPacket(positionLook);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void sendMessage(String message, UUID uuid) {
|
||||
sendMessage(TextComponent.fromLegacyText(message), uuid);
|
||||
}
|
||||
|
||||
public void sendMessage(BaseComponent component, UUID uuid) {
|
||||
sendMessage(new BaseComponent[] { component }, uuid);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendMessage(BaseComponent[] component, UUID uuid) {
|
||||
try {
|
||||
PacketPlayOutChat chat = new PacketPlayOutChat(ComponentSerializer.toString(component), 0, uuid);
|
||||
clientConnection.sendPacket(chat);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public void sendMessage(String message) {
|
||||
sendMessage(TextComponent.fromLegacyText(message));
|
||||
}
|
||||
|
||||
public void sendMessage(BaseComponent component) {
|
||||
sendMessage(new BaseComponent[] { component });
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendMessage(BaseComponent[] component) {
|
||||
try {
|
||||
PacketPlayOutChat chat = new PacketPlayOutChat(ComponentSerializer.toString(component), 0, new UUID(0, 0));
|
||||
clientConnection.sendPacket(chat);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public void disconnect() {
|
||||
disconnect("Disconnected!");
|
||||
}
|
||||
|
||||
public void disconnect(String reason) {
|
||||
disconnect(TextComponent.fromLegacyText(reason));
|
||||
}
|
||||
|
||||
public void disconnect(BaseComponent reason) {
|
||||
disconnect(new BaseComponent[] {reason});
|
||||
}
|
||||
|
||||
public void disconnect(BaseComponent[] reason) {
|
||||
clientConnection.disconnect(reason);
|
||||
}
|
||||
|
||||
public void chat(String message) {
|
||||
String prefix = "<" + username + "> ";
|
||||
PlayerChatEvent event = (PlayerChatEvent) Limbo.getInstance().getEventsManager().callEvent(new PlayerChatEvent(this, prefix, message, false));
|
||||
if (!event.isCancelled()) {
|
||||
String chat = event.getPrefix() + event.getMessage();
|
||||
Limbo.getInstance().getConsole().sendMessage(chat);
|
||||
for (Player each : Limbo.getInstance().getPlayers()) {
|
||||
each.sendMessage(chat, uuid);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package com.loohp.limbo.Player;
|
||||
|
||||
import com.loohp.limbo.Utils.GameMode;
|
||||
|
||||
@Deprecated
|
||||
public class Unsafe {
|
||||
|
||||
private Unsafe() {}
|
||||
|
||||
@Deprecated
|
||||
public void a(Player a, GameMode b) {
|
||||
a.gamemode = b;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public void a(Player a, int b) {
|
||||
a.entityId = b;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package com.loohp.limbo.Plugins;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import com.loohp.limbo.Limbo;
|
||||
import com.loohp.limbo.File.FileConfiguration;
|
||||
|
||||
public class LimboPlugin {
|
||||
|
||||
private String name;
|
||||
private File dataFolder;
|
||||
private PluginInfo info;
|
||||
|
||||
protected final void setInfo(FileConfiguration file) {
|
||||
info = new PluginInfo(file);
|
||||
name = info.getName();
|
||||
dataFolder = new File(Limbo.getInstance().getPluginFolder(), name);
|
||||
}
|
||||
|
||||
public void onLoad() {
|
||||
|
||||
}
|
||||
|
||||
public void onEnable() {
|
||||
|
||||
}
|
||||
|
||||
public void onDisable() {
|
||||
|
||||
}
|
||||
|
||||
public final String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public final File getDataFolder() {
|
||||
return new File(dataFolder.getAbsolutePath());
|
||||
}
|
||||
|
||||
public final PluginInfo getInfo() {
|
||||
return info;
|
||||
}
|
||||
|
||||
public final Limbo getServer() {
|
||||
return Limbo.getInstance();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package com.loohp.limbo.Plugins;
|
||||
|
||||
import com.loohp.limbo.File.FileConfiguration;
|
||||
|
||||
public class PluginInfo {
|
||||
|
||||
private String name;
|
||||
private String description;
|
||||
private String author;
|
||||
private String version;
|
||||
private String main;
|
||||
|
||||
public PluginInfo(FileConfiguration file) {
|
||||
name = file.get("name", String.class);
|
||||
description = file.get("description", String.class) == null ? "" : file.get("description", String.class);
|
||||
author = file.get("author", String.class);
|
||||
version = file.get("version", String.class);
|
||||
main = file.get("main", String.class);
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public String getAuthor() {
|
||||
return author;
|
||||
}
|
||||
|
||||
public String getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
public String getMainClass() {
|
||||
return main;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,142 @@
|
||||
package com.loohp.limbo.Plugins;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipInputStream;
|
||||
|
||||
import com.loohp.limbo.Limbo;
|
||||
import com.loohp.limbo.Commands.CommandExecutor;
|
||||
import com.loohp.limbo.Commands.CommandSender;
|
||||
import com.loohp.limbo.Commands.TabCompletor;
|
||||
import com.loohp.limbo.File.FileConfiguration;
|
||||
|
||||
public class PluginManager {
|
||||
|
||||
private Map<String, LimboPlugin> plugins;
|
||||
private List<Executor> executors;
|
||||
private File pluginFolder;
|
||||
|
||||
public PluginManager(File pluginFolder) {
|
||||
this.pluginFolder = pluginFolder;
|
||||
this.executors = new ArrayList<>();
|
||||
this.plugins = new LinkedHashMap<>();
|
||||
}
|
||||
|
||||
protected void loadPlugins() {
|
||||
for (File file : pluginFolder.listFiles()) {
|
||||
if (file.isFile() && file.getName().endsWith(".jar")) {
|
||||
boolean found = false;
|
||||
try (ZipInputStream zip = new ZipInputStream(new FileInputStream(file))) {
|
||||
while (true) {
|
||||
ZipEntry entry = zip.getNextEntry();
|
||||
if (entry == null) {
|
||||
break;
|
||||
}
|
||||
String name = entry.getName();
|
||||
if (name.endsWith("plugin.yml") || name.endsWith("limbo.yml")) {
|
||||
found = true;
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
FileConfiguration pluginYaml = new FileConfiguration(zip);
|
||||
String main = pluginYaml.get("main", String.class);
|
||||
String pluginName = pluginYaml.get("name", String.class);
|
||||
|
||||
if (plugins.containsKey(pluginName)) {
|
||||
System.err.println("Ambiguous plugin name in " + file.getName() + " with the plugin \"" + plugins.get(pluginName).getClass().getName() + "\"");
|
||||
break;
|
||||
}
|
||||
|
||||
URLClassLoader url = new URLClassLoader(new URL[] {file.toURI().toURL()});
|
||||
Class<?> clazz = url.loadClass(main);
|
||||
LimboPlugin plugin = (LimboPlugin) clazz.getDeclaredConstructor().newInstance();
|
||||
plugin.setInfo(pluginYaml);
|
||||
plugins.put(plugin.getName(), plugin);
|
||||
plugin.onLoad();
|
||||
Limbo.getInstance().getConsole().sendMessage("Loading plugin " + file.getName() + " " + plugin.getInfo().getVersion() + " by " + plugin.getInfo().getAuthor());
|
||||
url.close();
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
System.err.println("Unable to load plugin \"" + file.getName() + "\"");
|
||||
e.printStackTrace();
|
||||
}
|
||||
if (!found) {
|
||||
System.err.println("Jar file " + file.getName() + " has no plugin.yml!");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public List<LimboPlugin> getPlugins() {
|
||||
return new ArrayList<>(plugins.values());
|
||||
}
|
||||
|
||||
public LimboPlugin getPlugin(String name) {
|
||||
return plugins.get(name);
|
||||
}
|
||||
|
||||
public void fireExecutors(CommandSender sender, String[] args) throws Exception {
|
||||
Limbo.getInstance().getConsole().sendMessage(sender.getName() + " executed server command: /" + String.join(" ", args));
|
||||
for (Executor entry : executors) {
|
||||
try {
|
||||
entry.executor.execute(sender, args);
|
||||
} catch (Exception e) {
|
||||
System.err.println("Error while passing command \"" + args[0] + "\" to the plugin \"" + entry.plugin.getName() + "\"");
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public List<String> getTabOptions(CommandSender sender, String[] args) {
|
||||
List<String> options = new ArrayList<>();
|
||||
for (Executor entry : executors) {
|
||||
if (entry.tab.isPresent()) {
|
||||
try {
|
||||
options.addAll(entry.tab.get().tabComplete(sender, args));
|
||||
} catch (Exception e) {
|
||||
System.err.println("Error while passing tab completion to the plugin \"" + entry.plugin.getName() + "\"");
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
return options;
|
||||
}
|
||||
|
||||
public void registerCommands(LimboPlugin plugin, CommandExecutor executor) {
|
||||
executors.add(new Executor(plugin, executor));
|
||||
}
|
||||
|
||||
public void unregsiterAllCommands(LimboPlugin plugin) {
|
||||
executors.removeIf(each -> each.plugin.equals(plugin));
|
||||
}
|
||||
|
||||
public File getPluginFolder() {
|
||||
return new File(pluginFolder.getAbsolutePath());
|
||||
}
|
||||
|
||||
protected static class Executor {
|
||||
public LimboPlugin plugin;
|
||||
public CommandExecutor executor;
|
||||
public Optional<TabCompletor> tab;
|
||||
|
||||
public Executor(LimboPlugin plugin, CommandExecutor executor) {
|
||||
this.plugin = plugin;
|
||||
this.executor = executor;
|
||||
if (executor instanceof TabCompletor) {
|
||||
this.tab = Optional.of((TabCompletor) executor);
|
||||
} else {
|
||||
this.tab = Optional.empty();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,476 @@
|
||||
package com.loohp.limbo.Server;
|
||||
|
||||
import java.io.DataInputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.net.InetAddress;
|
||||
import java.net.Socket;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import com.loohp.limbo.DeclareCommands;
|
||||
import com.loohp.limbo.Limbo;
|
||||
import com.loohp.limbo.Events.PlayerJoinEvent;
|
||||
import com.loohp.limbo.Events.PlayerLoginEvent;
|
||||
import com.loohp.limbo.Events.PlayerMoveEvent;
|
||||
import com.loohp.limbo.Events.PlayerQuitEvent;
|
||||
import com.loohp.limbo.Events.StatusPingEvent;
|
||||
import com.loohp.limbo.File.ServerProperties;
|
||||
import com.loohp.limbo.Location.Location;
|
||||
import com.loohp.limbo.Player.Player;
|
||||
import com.loohp.limbo.Server.Packets.Packet;
|
||||
import com.loohp.limbo.Server.Packets.PacketHandshakingIn;
|
||||
import com.loohp.limbo.Server.Packets.PacketLoginInLoginStart;
|
||||
import com.loohp.limbo.Server.Packets.PacketLoginOutDisconnect;
|
||||
import com.loohp.limbo.Server.Packets.PacketLoginOutLoginSuccess;
|
||||
import com.loohp.limbo.Server.Packets.PacketOut;
|
||||
import com.loohp.limbo.Server.Packets.PacketPlayInChat;
|
||||
import com.loohp.limbo.Server.Packets.PacketPlayInKeepAlive;
|
||||
import com.loohp.limbo.Server.Packets.PacketPlayInPosition;
|
||||
import com.loohp.limbo.Server.Packets.PacketPlayInPositionAndLook;
|
||||
import com.loohp.limbo.Server.Packets.PacketPlayInRotation;
|
||||
import com.loohp.limbo.Server.Packets.PacketPlayInTabComplete;
|
||||
import com.loohp.limbo.Server.Packets.PacketPlayOutDeclareCommands;
|
||||
import com.loohp.limbo.Server.Packets.PacketPlayOutDisconnect;
|
||||
import com.loohp.limbo.Server.Packets.PacketPlayOutLightUpdate;
|
||||
import com.loohp.limbo.Server.Packets.PacketPlayOutLogin;
|
||||
import com.loohp.limbo.Server.Packets.PacketPlayOutMapChunk;
|
||||
import com.loohp.limbo.Server.Packets.PacketPlayOutPlayerAbilities;
|
||||
import com.loohp.limbo.Server.Packets.PacketPlayOutPlayerAbilities.PlayerAbilityFlags;
|
||||
import com.loohp.limbo.Server.Packets.PacketPlayOutPlayerInfo;
|
||||
import com.loohp.limbo.Server.Packets.PacketPlayOutPlayerInfo.PlayerInfoAction;
|
||||
import com.loohp.limbo.Server.Packets.PacketPlayOutPlayerInfo.PlayerInfoData;
|
||||
import com.loohp.limbo.Server.Packets.PacketPlayOutPlayerInfo.PlayerInfoData.PlayerInfoDataAddPlayer.PlayerSkinProperty;
|
||||
import com.loohp.limbo.Server.Packets.PacketPlayOutPositionAndLook;
|
||||
import com.loohp.limbo.Server.Packets.PacketPlayOutShowPlayerSkins;
|
||||
import com.loohp.limbo.Server.Packets.PacketPlayOutSpawnPosition;
|
||||
import com.loohp.limbo.Server.Packets.PacketPlayOutTabComplete;
|
||||
import com.loohp.limbo.Server.Packets.PacketPlayOutTabComplete.TabCompleteMatches;
|
||||
import com.loohp.limbo.Server.Packets.PacketPlayOutUpdateViewPosition;
|
||||
import com.loohp.limbo.Server.Packets.PacketStatusInPing;
|
||||
import com.loohp.limbo.Server.Packets.PacketStatusInRequest;
|
||||
import com.loohp.limbo.Server.Packets.PacketStatusOutPong;
|
||||
import com.loohp.limbo.Server.Packets.PacketStatusOutResponse;
|
||||
import com.loohp.limbo.Utils.CustomStringUtils;
|
||||
import com.loohp.limbo.Utils.DataTypeIO;
|
||||
import com.loohp.limbo.Utils.GameMode;
|
||||
import com.loohp.limbo.Utils.MojangAPIUtils;
|
||||
import com.loohp.limbo.Utils.MojangAPIUtils.SkinResponse;
|
||||
import com.loohp.limbo.Utils.NamespacedKey;
|
||||
import com.loohp.limbo.World.BlockPosition;
|
||||
import com.loohp.limbo.World.World;
|
||||
|
||||
import net.md_5.bungee.api.ChatColor;
|
||||
import net.md_5.bungee.api.chat.BaseComponent;
|
||||
import net.md_5.bungee.api.chat.TextComponent;
|
||||
import net.md_5.bungee.chat.ComponentSerializer;
|
||||
import net.querz.mca.Chunk;
|
||||
|
||||
public class ClientConnection extends Thread {
|
||||
|
||||
public enum ClientState {
|
||||
LEGACY,
|
||||
HANDSHAKE,
|
||||
STATUS,
|
||||
LOGIN,
|
||||
PLAY,
|
||||
DISCONNECTED;
|
||||
}
|
||||
|
||||
private final Socket client_socket;
|
||||
private boolean running;
|
||||
private ClientState state;
|
||||
|
||||
private Player player;
|
||||
private long lastKeepAlivePayLoad;
|
||||
|
||||
protected DataOutputStream output;
|
||||
protected DataInputStream input;
|
||||
|
||||
private InetAddress inetAddress;
|
||||
|
||||
public ClientConnection(Socket client_socket) {
|
||||
this.client_socket = client_socket;
|
||||
this.inetAddress = client_socket.getInetAddress();
|
||||
}
|
||||
|
||||
public InetAddress getInetAddress() {
|
||||
return inetAddress;
|
||||
}
|
||||
|
||||
public long getLastKeepAlivePayLoad() {
|
||||
return lastKeepAlivePayLoad;
|
||||
}
|
||||
|
||||
public void setLastKeepAlivePayLoad(long payLoad) {
|
||||
this.lastKeepAlivePayLoad = payLoad;
|
||||
}
|
||||
|
||||
public Player getPlayer() {
|
||||
return player;
|
||||
}
|
||||
|
||||
public ClientState getClientState() {
|
||||
return state;
|
||||
}
|
||||
|
||||
public Socket getSocket() {
|
||||
return client_socket;
|
||||
}
|
||||
|
||||
public boolean isRunning() {
|
||||
return running;
|
||||
}
|
||||
|
||||
public void sendPacket(PacketOut packet) throws IOException {
|
||||
byte[] packetByte = packet.serializePacket();
|
||||
DataTypeIO.writeVarInt(output, packetByte.length);
|
||||
output.write(packetByte);
|
||||
}
|
||||
|
||||
public void disconnect(BaseComponent[] reason) {
|
||||
try {
|
||||
PacketPlayOutDisconnect packet = new PacketPlayOutDisconnect(ComponentSerializer.toString(reason));
|
||||
byte[] packetByte = packet.serializePacket();
|
||||
DataTypeIO.writeVarInt(output, packetByte.length);
|
||||
output.write(packetByte);
|
||||
output.flush();
|
||||
} catch (IOException e) {}
|
||||
try {
|
||||
client_socket.close();
|
||||
} catch (IOException e) {}
|
||||
}
|
||||
|
||||
public void disconnectDuringLogin(BaseComponent[] reason) {
|
||||
try {
|
||||
PacketLoginOutDisconnect packet = new PacketLoginOutDisconnect(ComponentSerializer.toString(reason));
|
||||
byte[] packetByte = packet.serializePacket();
|
||||
DataTypeIO.writeVarInt(output, packetByte.length);
|
||||
output.write(packetByte);
|
||||
output.flush();
|
||||
} catch (IOException e) {}
|
||||
try {
|
||||
client_socket.close();
|
||||
} catch (IOException e) {}
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
@Override
|
||||
public void run() {
|
||||
running = true;
|
||||
state = ClientState.HANDSHAKE;
|
||||
try {
|
||||
client_socket.setKeepAlive(true);
|
||||
input = new DataInputStream(client_socket.getInputStream());
|
||||
output = new DataOutputStream(client_socket.getOutputStream());
|
||||
int handShakeSize = DataTypeIO.readVarInt(input);
|
||||
|
||||
//legacy ping
|
||||
if (handShakeSize == 0xFE) {
|
||||
state = ClientState.LEGACY;
|
||||
output.writeByte(255);
|
||||
String str = client_socket.getInetAddress().getHostName() + ":" + client_socket.getPort();
|
||||
Limbo.getInstance().getConsole().sendMessage("[/" + str + "] <-> Legacy Status has pinged");
|
||||
ServerProperties p = Limbo.getInstance().getServerProperties();
|
||||
StatusPingEvent event = Limbo.getInstance().getEventsManager().callEvent(new StatusPingEvent(this, p.getVersionString(), p.getProtocol(), ComponentSerializer.parse(p.getMotdJson()), p.getMaxPlayers(), Limbo.getInstance().getPlayers().size(), p.getFavicon().orElse(null)));
|
||||
String response = Limbo.getInstance().buildLegacyPingResponse(event.getVersion(), event.getMotd(), event.getMaxPlayers(), event.getPlayersOnline());
|
||||
byte[] bytes = response.getBytes(StandardCharsets.UTF_16BE);
|
||||
output.writeShort(response.length());
|
||||
output.write(bytes);
|
||||
|
||||
client_socket.close();
|
||||
state = ClientState.DISCONNECTED;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
int handShakeId = DataTypeIO.readVarInt(input);
|
||||
|
||||
PacketHandshakingIn handshake = new PacketHandshakingIn(input);
|
||||
|
||||
boolean isBungeecord = Limbo.getInstance().getServerProperties().isBungeecord();
|
||||
String bungeeForwarding = handshake.getServerAddress();
|
||||
UUID bungeeUUID = null;
|
||||
SkinResponse bungeeSkin = null;
|
||||
|
||||
switch (handshake.getHandshakeType()) {
|
||||
case STATUS:
|
||||
state = ClientState.STATUS;
|
||||
while (client_socket.isConnected()) {
|
||||
DataTypeIO.readVarInt(input);
|
||||
int packetId = DataTypeIO.readVarInt(input);
|
||||
Class<? extends Packet> packetType = Packet.getStatusIn().get(packetId);
|
||||
if (packetType == null) {
|
||||
//do nothing
|
||||
} else if (packetType.equals(PacketStatusInRequest.class)) {
|
||||
String str = client_socket.getInetAddress().getHostName() + ":" + client_socket.getPort();
|
||||
Limbo.getInstance().getConsole().sendMessage("[/" + str + "] <-> Handshake Status has pinged");
|
||||
ServerProperties p = Limbo.getInstance().getServerProperties();
|
||||
StatusPingEvent event = Limbo.getInstance().getEventsManager().callEvent(new StatusPingEvent(this, p.getVersionString(), p.getProtocol(), ComponentSerializer.parse(p.getMotdJson()), p.getMaxPlayers(), Limbo.getInstance().getPlayers().size(), p.getFavicon().orElse(null)));
|
||||
PacketStatusOutResponse packet = new PacketStatusOutResponse(Limbo.getInstance().buildServerListResponseJson(event.getVersion(), event.getProtocol(), event.getMotd(), event.getMaxPlayers(), event.getPlayersOnline(), event.getFavicon()));
|
||||
sendPacket(packet);
|
||||
} else if (packetType.equals(PacketStatusInPing.class)) {
|
||||
PacketStatusInPing ping = new PacketStatusInPing(input);
|
||||
PacketStatusOutPong packet = new PacketStatusOutPong(ping.getPayload());
|
||||
sendPacket(packet);
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case LOGIN:
|
||||
state = ClientState.LOGIN;
|
||||
|
||||
if (isBungeecord) {
|
||||
try {
|
||||
String[] data = bungeeForwarding.split("\\x00");
|
||||
//String host = data[0];
|
||||
String ip = data[1];
|
||||
|
||||
bungeeUUID = UUID.fromString(data[2].replaceFirst("([0-9a-fA-F]{8})([0-9a-fA-F]{4})([0-9a-fA-F]{4})([0-9a-fA-F]{4})([0-9a-fA-F]+)", "$1-$2-$3-$4-$5"));
|
||||
inetAddress = InetAddress.getByName(ip);
|
||||
|
||||
String skinJson = data[3];
|
||||
|
||||
String skin = skinJson.split("\"value\":\"")[1].split("\"")[0];
|
||||
String signature = skinJson.split("\"signature\":\"")[1].split("\"")[0];
|
||||
bungeeSkin = new SkinResponse(skin, signature);
|
||||
} catch (Exception e) {
|
||||
Limbo.getInstance().getConsole().sendMessage("If you wish to use bungeecord's IP forwarding, please enable that in your bungeecord config.yml as well!");
|
||||
disconnectDuringLogin(new BaseComponent[] {new TextComponent(ChatColor.RED + "Please connect from the proxy!")});
|
||||
}
|
||||
}
|
||||
|
||||
while (client_socket.isConnected()) {
|
||||
int size = DataTypeIO.readVarInt(input);
|
||||
int packetId = DataTypeIO.readVarInt(input);
|
||||
Class<? extends Packet> packetType = Packet.getLoginIn().get(packetId);
|
||||
|
||||
if (packetType == null) {
|
||||
input.skipBytes(size - DataTypeIO.getVarIntLength(packetId));
|
||||
} else if (packetType.equals(PacketLoginInLoginStart.class)) {
|
||||
PacketLoginInLoginStart start = new PacketLoginInLoginStart(input);
|
||||
String username = start.getUsername();
|
||||
UUID uuid = isBungeecord ? bungeeUUID : UUID.nameUUIDFromBytes(("OfflinePlayer:" + username).getBytes(StandardCharsets.UTF_8));
|
||||
|
||||
PacketLoginOutLoginSuccess success = new PacketLoginOutLoginSuccess(uuid, username);
|
||||
sendPacket(success);
|
||||
|
||||
state = ClientState.PLAY;
|
||||
|
||||
player = new Player(this, username, uuid, Limbo.getInstance().getNextEntityId(), Limbo.getInstance().getServerProperties().getWorldSpawn());
|
||||
Limbo.getInstance().addPlayer(player);
|
||||
|
||||
break;
|
||||
} else {
|
||||
input.skipBytes(size - DataTypeIO.getVarIntLength(packetId));
|
||||
}
|
||||
}
|
||||
|
||||
PlayerLoginEvent event = Limbo.getInstance().getEventsManager().callEvent(new PlayerLoginEvent(this, false));
|
||||
if (event.isCancelled()) {
|
||||
disconnectDuringLogin(event.getCancelReason());
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (state == ClientState.PLAY) {
|
||||
|
||||
TimeUnit.MILLISECONDS.sleep(500);
|
||||
|
||||
ServerProperties p = Limbo.getInstance().getServerProperties();
|
||||
PacketPlayOutLogin join = new PacketPlayOutLogin(player.getEntityId(), false, p.getDefaultGamemode(), Limbo.getInstance().getWorlds().stream().map(each -> new NamespacedKey(each.getName()).toString()).collect(Collectors.toList()).toArray(new String[Limbo.getInstance().getWorlds().size()]), Limbo.getInstance().getDimensionRegistry().getCodec(), p.getWorldSpawn().getWorld(), 0, (byte) p.getMaxPlayers(), 8, p.isReducedDebugInfo(), true, false, true);
|
||||
sendPacket(join);
|
||||
Limbo.getInstance().getUnsafe().setPlayerGameModeSilently(player, p.getDefaultGamemode());
|
||||
|
||||
Location s = p.getWorldSpawn();
|
||||
|
||||
//PacketPlayOutKeepAlive alive = new PacketPlayOutKeepAlive((long) (Math.random() * Long.MAX_VALUE));
|
||||
|
||||
World world = s.getWorld();
|
||||
|
||||
for (int x = 0; x < world.getChunks().length; x++) {
|
||||
for (int z = 0; z < world.getChunks()[x].length; z++) {
|
||||
Chunk chunk = world.getChunks()[x][z];
|
||||
if (chunk != null) {
|
||||
PacketPlayOutMapChunk chunkdata = new PacketPlayOutMapChunk(x, z, chunk, world.getEnvironment());
|
||||
sendPacket(chunkdata);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int x = 0; x < world.getChunks().length; x++) {
|
||||
for (int z = 0; z < world.getChunks()[x].length; z++) {
|
||||
List<Byte[]> blockChunk = world.getLightEngineBlock().getBlockLightBitMask(x, z);
|
||||
if (blockChunk == null) {
|
||||
blockChunk = new ArrayList<>();
|
||||
}
|
||||
List<Byte[]> skyChunk = null;
|
||||
if (world.hasSkyLight()) {
|
||||
skyChunk = world.getLightEngineSky().getSkyLightBitMask(x, z);
|
||||
}
|
||||
if (skyChunk == null) {
|
||||
skyChunk = new ArrayList<>();
|
||||
}
|
||||
PacketPlayOutLightUpdate chunkdata = new PacketPlayOutLightUpdate(x, z, true, skyChunk, blockChunk);
|
||||
sendPacket(chunkdata);
|
||||
}
|
||||
}
|
||||
|
||||
SkinResponse skinresponce = isBungeecord ? bungeeSkin : MojangAPIUtils.getSkinFromMojangServer(player.getName());
|
||||
PlayerSkinProperty skin = skinresponce != null ? new PlayerSkinProperty(skinresponce.getSkin(), skinresponce.getSignature()) : null;
|
||||
PacketPlayOutPlayerInfo info = new PacketPlayOutPlayerInfo(PlayerInfoAction.ADD_PLAYER, player.getUUID(), new PlayerInfoData.PlayerInfoDataAddPlayer(player.getName(), Optional.ofNullable(skin), p.getDefaultGamemode(), 0, false, Optional.empty()));
|
||||
sendPacket(info);
|
||||
/*
|
||||
for (ClientConnection client : Limbo.getInstance().getServerConnection().getClients()) {
|
||||
DataOutputStream other = new DataOutputStream(client.getSocket().getOutputStream());
|
||||
DataTypeIO.writeVarInt(other, packetByte.length);
|
||||
other.write(packetByte);
|
||||
}
|
||||
*/
|
||||
|
||||
PacketPlayOutShowPlayerSkins show = new PacketPlayOutShowPlayerSkins(player.getEntityId());
|
||||
sendPacket(show);
|
||||
|
||||
Set<PlayerAbilityFlags> flags = new HashSet<>();
|
||||
if (p.isAllowFlight()) {
|
||||
flags.add(PlayerAbilityFlags.FLY);
|
||||
}
|
||||
if (player.getGamemode().equals(GameMode.CREATIVE)) {
|
||||
flags.add(PlayerAbilityFlags.CREATIVE);
|
||||
}
|
||||
PacketPlayOutPlayerAbilities abilities = new PacketPlayOutPlayerAbilities(0.05F, 0.1F, flags.toArray(new PlayerAbilityFlags[flags.size()]));
|
||||
sendPacket(abilities);
|
||||
|
||||
String str = client_socket.getInetAddress().getHostName() + ":" + client_socket.getPort() + "|" + player.getName();
|
||||
Limbo.getInstance().getConsole().sendMessage("[/" + str + "] <-> Player had connected to the Limbo server!");
|
||||
|
||||
PacketPlayOutDeclareCommands declare = DeclareCommands.getDeclareCommandsPacket(player);
|
||||
if (declare != null) {
|
||||
sendPacket(declare);
|
||||
}
|
||||
|
||||
Limbo.getInstance().getEventsManager().callEvent(new PlayerJoinEvent(player));
|
||||
|
||||
PacketPlayOutSpawnPosition spawnPos = new PacketPlayOutSpawnPosition(BlockPosition.from(s));
|
||||
sendPacket(spawnPos);
|
||||
|
||||
PacketPlayOutPositionAndLook positionLook = new PacketPlayOutPositionAndLook(s.getX(), s.getY(), s.getZ(), s.getYaw(), s.getPitch(), 1);
|
||||
player.setLocation(new Location(world, s.getX(), s.getY(), s.getZ(), s.getYaw(), s.getPitch()));
|
||||
sendPacket(positionLook);
|
||||
|
||||
while (client_socket.isConnected()) {
|
||||
try {
|
||||
int size = DataTypeIO.readVarInt(input);
|
||||
int packetId = DataTypeIO.readVarInt(input);
|
||||
Class<? extends Packet> packetType = Packet.getPlayIn().get(packetId);
|
||||
//Limbo.getInstance().getConsole().sendMessage(packetId + " -> " + packetType);
|
||||
if (packetType == null) {
|
||||
input.skipBytes(size - DataTypeIO.getVarIntLength(packetId));
|
||||
} else if (packetType.equals(PacketPlayInPositionAndLook.class)) {
|
||||
PacketPlayInPositionAndLook pos = new PacketPlayInPositionAndLook(input);
|
||||
Location from = player.getLocation();
|
||||
Location to = new Location(player.getWorld(), pos.getX(), pos.getY(), pos.getZ(), pos.getYaw(), pos.getPitch());
|
||||
|
||||
PlayerMoveEvent event = Limbo.getInstance().getEventsManager().callEvent(new PlayerMoveEvent(player, from, to));
|
||||
if (event.isCancelled()) {
|
||||
Location returnTo = event.getFrom();
|
||||
PacketPlayOutPositionAndLook cancel = new PacketPlayOutPositionAndLook(returnTo.getX(), returnTo.getY(), returnTo.getZ(), returnTo.getYaw(), returnTo.getPitch(), 1);
|
||||
sendPacket(cancel);
|
||||
} else {
|
||||
player.setLocation(event.getTo());
|
||||
PacketPlayOutUpdateViewPosition response = new PacketPlayOutUpdateViewPosition((int) player.getLocation().getX() >> 4, (int) player.getLocation().getZ() >> 4);
|
||||
sendPacket(response);
|
||||
}
|
||||
} else if (packetType.equals(PacketPlayInPosition.class)) {
|
||||
PacketPlayInPosition pos = new PacketPlayInPosition(input);
|
||||
Location from = player.getLocation();
|
||||
Location to = new Location(player.getWorld(), pos.getX(), pos.getY(), pos.getZ(), player.getLocation().getYaw(), player.getLocation().getPitch());
|
||||
|
||||
PlayerMoveEvent event = Limbo.getInstance().getEventsManager().callEvent(new PlayerMoveEvent(player, from, to));
|
||||
if (event.isCancelled()) {
|
||||
Location returnTo = event.getFrom();
|
||||
PacketPlayOutPositionAndLook cancel = new PacketPlayOutPositionAndLook(returnTo.getX(), returnTo.getY(), returnTo.getZ(), returnTo.getYaw(), returnTo.getPitch(), 1);
|
||||
sendPacket(cancel);
|
||||
} else {
|
||||
player.setLocation(event.getTo());
|
||||
PacketPlayOutUpdateViewPosition response = new PacketPlayOutUpdateViewPosition((int) player.getLocation().getX() >> 4, (int) player.getLocation().getZ() >> 4);
|
||||
sendPacket(response);
|
||||
}
|
||||
} else if (packetType.equals(PacketPlayInRotation.class)) {
|
||||
PacketPlayInRotation pos = new PacketPlayInRotation(input);
|
||||
Location from = player.getLocation();
|
||||
Location to = new Location(player.getWorld(), player.getLocation().getX(), player.getLocation().getY(), player.getLocation().getZ(), pos.getYaw(), pos.getPitch());
|
||||
|
||||
PlayerMoveEvent event = Limbo.getInstance().getEventsManager().callEvent(new PlayerMoveEvent(player, from, to));
|
||||
if (event.isCancelled()) {
|
||||
Location returnTo = event.getFrom();
|
||||
PacketPlayOutPositionAndLook cancel = new PacketPlayOutPositionAndLook(returnTo.getX(), returnTo.getY(), returnTo.getZ(), returnTo.getYaw(), returnTo.getPitch(), 1);
|
||||
sendPacket(cancel);
|
||||
} else {
|
||||
player.setLocation(event.getTo());
|
||||
PacketPlayOutUpdateViewPosition response = new PacketPlayOutUpdateViewPosition((int) player.getLocation().getX() >> 4, (int) player.getLocation().getZ() >> 4);
|
||||
sendPacket(response);
|
||||
}
|
||||
} else if (packetType.equals(PacketPlayInKeepAlive.class)) {
|
||||
PacketPlayInKeepAlive alive = new PacketPlayInKeepAlive(input);
|
||||
if (alive.getPayload() != lastKeepAlivePayLoad) {
|
||||
Limbo.getInstance().getConsole().sendMessage("Incorrect Payload recieved in KeepAlive packet for player " + player.getName());
|
||||
break;
|
||||
}
|
||||
} else if (packetType.equals(PacketPlayInTabComplete.class)) {
|
||||
PacketPlayInTabComplete request = new PacketPlayInTabComplete(input);
|
||||
String[] command = CustomStringUtils.splitStringToArgs(request.getText().substring(1));
|
||||
|
||||
List<TabCompleteMatches> matches = new ArrayList<TabCompleteMatches>();
|
||||
|
||||
matches.addAll(Limbo.getInstance().getPluginManager().getTabOptions(player, command).stream().map(each -> new TabCompleteMatches(each)).collect(Collectors.toList()));
|
||||
|
||||
int start = CustomStringUtils.getIndexOfArg(request.getText(), command.length - 1) + 1;
|
||||
int length = command[command.length - 1].length();
|
||||
|
||||
PacketPlayOutTabComplete response = new PacketPlayOutTabComplete(request.getId(), start, length, matches.toArray(new TabCompleteMatches[matches.size()]));
|
||||
sendPacket(response);
|
||||
} else if (packetType.equals(PacketPlayInChat.class)) {
|
||||
PacketPlayInChat chat = new PacketPlayInChat(input);
|
||||
if (chat.getMessage().startsWith("/")) {
|
||||
Limbo.getInstance().dispatchCommand(player, chat.getMessage());
|
||||
} else {
|
||||
player.chat(chat.getMessage());
|
||||
}
|
||||
} else {
|
||||
input.skipBytes(size - DataTypeIO.getVarIntLength(packetId));
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Limbo.getInstance().getEventsManager().callEvent(new PlayerQuitEvent(player));
|
||||
|
||||
str = client_socket.getInetAddress().getHostName() + ":" + client_socket.getPort() + "|" + player.getName();
|
||||
Limbo.getInstance().getConsole().sendMessage("[/" + str + "] <-> Player had disconnected!");
|
||||
|
||||
}
|
||||
|
||||
} catch (Exception e) {}
|
||||
|
||||
try {
|
||||
client_socket.close();
|
||||
} catch (IOException e) {}
|
||||
state = ClientState.DISCONNECTED;
|
||||
|
||||
if (player != null) {
|
||||
Limbo.getInstance().removePlayer(player);
|
||||
}
|
||||
Limbo.getInstance().getServerConnection().getClients().remove(this);
|
||||
running = false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package com.loohp.limbo.Server;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import com.loohp.limbo.Limbo;
|
||||
import com.loohp.limbo.Server.ClientConnection.ClientState;
|
||||
import com.loohp.limbo.Server.Packets.PacketPlayOutKeepAlive;
|
||||
import com.loohp.limbo.Utils.DataTypeIO;
|
||||
|
||||
public class KeepAliveSender extends Thread {
|
||||
|
||||
private Random random;
|
||||
|
||||
public KeepAliveSender() {
|
||||
random = new Random();
|
||||
start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
while (true) {
|
||||
try {
|
||||
for (ClientConnection client : Limbo.getInstance().getServerConnection().getClients()) {
|
||||
if (client.getClientState().equals(ClientState.PLAY)) {
|
||||
try {
|
||||
PacketPlayOutKeepAlive packet = new PacketPlayOutKeepAlive(random.nextLong());
|
||||
byte[] packetByte = packet.serializePacket();
|
||||
DataTypeIO.writeVarInt(client.output, packetByte.length);
|
||||
client.output.write(packetByte);
|
||||
client.setLastKeepAlivePayLoad(packet.getPayload());
|
||||
} catch (IOException ignore) {}
|
||||
}
|
||||
}
|
||||
TimeUnit.SECONDS.sleep(5);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
package com.loohp.limbo.Server.Packets;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public class Packet {
|
||||
|
||||
private static Map<Integer, Class<? extends PacketIn>> HandshakeIn;
|
||||
|
||||
private static Map<Integer, Class<? extends PacketIn>> StatusIn;
|
||||
private static Map<Class<? extends PacketOut>, Integer> StatusOut;
|
||||
|
||||
private static Map<Integer, Class<? extends PacketIn>> LoginIn;
|
||||
private static Map<Class<? extends PacketOut>, Integer> LoginOut;
|
||||
|
||||
private static Map<Integer, Class<? extends PacketIn>> PlayIn;
|
||||
private static Map<Class<? extends PacketOut>, Integer> PlayOut;
|
||||
|
||||
public static Map<Integer, Class<? extends PacketIn>> getHandshakeIn() {
|
||||
return HandshakeIn;
|
||||
}
|
||||
|
||||
public static void setHandshakeIn(Map<Integer, Class<? extends PacketIn>> handshakeIn) {
|
||||
HandshakeIn = handshakeIn;
|
||||
}
|
||||
|
||||
public static Map<Integer, Class<? extends PacketIn>> getStatusIn() {
|
||||
return StatusIn;
|
||||
}
|
||||
|
||||
public static void setStatusIn(Map<Integer, Class<? extends PacketIn>> statusIn) {
|
||||
StatusIn = statusIn;
|
||||
}
|
||||
|
||||
public static Map<Class<? extends PacketOut>, Integer> getStatusOut() {
|
||||
return StatusOut;
|
||||
}
|
||||
|
||||
public static void setStatusOut(Map<Class<? extends PacketOut>, Integer> statusOut) {
|
||||
StatusOut = statusOut;
|
||||
}
|
||||
|
||||
public static Map<Integer, Class<? extends PacketIn>> getLoginIn() {
|
||||
return LoginIn;
|
||||
}
|
||||
|
||||
public static void setLoginIn(Map<Integer, Class<? extends PacketIn>> loginIn) {
|
||||
LoginIn = loginIn;
|
||||
}
|
||||
|
||||
public static Map<Class<? extends PacketOut>, Integer> getLoginOut() {
|
||||
return LoginOut;
|
||||
}
|
||||
|
||||
public static void setLoginOut(Map<Class<? extends PacketOut>, Integer> loginOut) {
|
||||
LoginOut = loginOut;
|
||||
}
|
||||
|
||||
public static Map<Integer, Class<? extends PacketIn>> getPlayIn() {
|
||||
return PlayIn;
|
||||
}
|
||||
|
||||
public static void setPlayIn(Map<Integer, Class<? extends PacketIn>> playIn) {
|
||||
PlayIn = playIn;
|
||||
}
|
||||
|
||||
public static Map<Class<? extends PacketOut>, Integer> getPlayOut() {
|
||||
return PlayOut;
|
||||
}
|
||||
|
||||
public static void setPlayOut(Map<Class<? extends PacketOut>, Integer> playOut) {
|
||||
PlayOut = playOut;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
package com.loohp.limbo.Server.Packets;
|
||||
|
||||
import java.io.DataInputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
import com.loohp.limbo.Utils.DataTypeIO;
|
||||
|
||||
public class PacketHandshakingIn extends PacketIn {
|
||||
|
||||
public static enum HandshakeType {
|
||||
STATUS(1),
|
||||
LOGIN(2);
|
||||
|
||||
int networkId;
|
||||
|
||||
HandshakeType(int networkId) {
|
||||
this.networkId = networkId;
|
||||
}
|
||||
|
||||
public int getNetworkId() {
|
||||
return networkId;
|
||||
}
|
||||
|
||||
public static HandshakeType fromNetworkId(int networkId) {
|
||||
for (HandshakeType type : HandshakeType.values()) {
|
||||
if (type.getNetworkId() == networkId) {
|
||||
return type;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
//==============================
|
||||
|
||||
private int protocolVersion;
|
||||
private String serverAddress;
|
||||
private int serverPort;
|
||||
private HandshakeType handshakeType;
|
||||
|
||||
public PacketHandshakingIn(int protocolVersion, String serverAddress, int serverPort, HandshakeType handshakeType) {
|
||||
this.protocolVersion = protocolVersion;
|
||||
this.serverAddress = serverAddress;
|
||||
this.serverPort = serverPort;
|
||||
this.handshakeType = handshakeType;
|
||||
}
|
||||
|
||||
public PacketHandshakingIn(DataInputStream in) throws IOException {
|
||||
this(DataTypeIO.readVarInt(in), DataTypeIO.readString(in, StandardCharsets.UTF_8), in.readShort() & 0xFFFF, HandshakeType.fromNetworkId(DataTypeIO.readVarInt(in)));
|
||||
}
|
||||
|
||||
public int getProtocolVersion() {
|
||||
return protocolVersion;
|
||||
}
|
||||
|
||||
public String getServerAddress() {
|
||||
return serverAddress;
|
||||
}
|
||||
|
||||
public int getServerPort() {
|
||||
return serverPort;
|
||||
}
|
||||
|
||||
public HandshakeType getHandshakeType() {
|
||||
return handshakeType;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package com.loohp.limbo.Server.Packets;
|
||||
|
||||
public abstract class PacketIn extends Packet {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package com.loohp.limbo.Server.Packets;
|
||||
|
||||
import java.io.DataInputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
import com.loohp.limbo.Utils.DataTypeIO;
|
||||
|
||||
public class PacketLoginInLoginStart extends PacketIn {
|
||||
|
||||
private String username;
|
||||
|
||||
public PacketLoginInLoginStart(String username) {
|
||||
this.username = username;
|
||||
}
|
||||
|
||||
public PacketLoginInLoginStart(DataInputStream in) throws IOException {
|
||||
this(DataTypeIO.readString(in, StandardCharsets.UTF_8));
|
||||
}
|
||||
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package com.loohp.limbo.Server.Packets;
|
||||
|
||||
import java.io.DataInputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
import com.loohp.limbo.Utils.DataTypeIO;
|
||||
import com.loohp.limbo.Utils.NamespacedKey;
|
||||
|
||||
public class PacketLoginInPluginMessaging extends PacketIn {
|
||||
|
||||
private int messageId;
|
||||
private NamespacedKey channel;
|
||||
private byte[] data;
|
||||
|
||||
public PacketLoginInPluginMessaging(int messageId, NamespacedKey channel, byte[] data) {
|
||||
this.messageId = messageId;
|
||||
this.channel = channel;
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
public PacketLoginInPluginMessaging(DataInputStream in, int packetLength, int packetId) throws IOException {
|
||||
messageId = DataTypeIO.readVarInt(in);
|
||||
String rawChannel = DataTypeIO.readString(in, StandardCharsets.UTF_8);
|
||||
channel = new NamespacedKey(rawChannel);
|
||||
int dataLength = packetLength - DataTypeIO.getVarIntLength(packetId) - DataTypeIO.getVarIntLength(messageId) - DataTypeIO.getStringLength(rawChannel, StandardCharsets.UTF_8);
|
||||
data = new byte[dataLength];
|
||||
in.read(data);
|
||||
}
|
||||
|
||||
public int getMessageId() {
|
||||
return messageId;
|
||||
}
|
||||
|
||||
public NamespacedKey getChannel() {
|
||||
return channel;
|
||||
}
|
||||
|
||||
public byte[] getData() {
|
||||
return data;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package com.loohp.limbo.Server.Packets;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
import com.loohp.limbo.Utils.DataTypeIO;
|
||||
|
||||
public class PacketLoginOutDisconnect extends PacketOut {
|
||||
|
||||
private String jsonReason;
|
||||
|
||||
public PacketLoginOutDisconnect(String jsonReason) {
|
||||
this.jsonReason = jsonReason;
|
||||
}
|
||||
|
||||
public String getJsonReason() {
|
||||
return jsonReason;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] serializePacket() throws IOException {
|
||||
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
|
||||
|
||||
DataOutputStream output = new DataOutputStream(buffer);
|
||||
output.writeByte(Packet.getLoginOut().get(getClass()));
|
||||
DataTypeIO.writeString(output, jsonReason, StandardCharsets.UTF_8);
|
||||
|
||||
return buffer.toByteArray();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package com.loohp.limbo.Server.Packets;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.UUID;
|
||||
|
||||
import com.loohp.limbo.Utils.DataTypeIO;
|
||||
|
||||
public class PacketLoginOutLoginSuccess extends PacketOut {
|
||||
|
||||
private UUID uuid;
|
||||
private String username;
|
||||
|
||||
public PacketLoginOutLoginSuccess(UUID uuid, String username) {
|
||||
this.uuid = uuid;
|
||||
this.username = username;
|
||||
}
|
||||
|
||||
public UUID getUuid() {
|
||||
return uuid;
|
||||
}
|
||||
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] serializePacket() throws IOException {
|
||||
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
|
||||
|
||||
DataOutputStream output = new DataOutputStream(buffer);
|
||||
output.writeByte(Packet.getLoginOut().get(getClass()));
|
||||
DataTypeIO.writeUUID(output, uuid);
|
||||
DataTypeIO.writeString(output, username, StandardCharsets.UTF_8);
|
||||
|
||||
return buffer.toByteArray();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package com.loohp.limbo.Server.Packets;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
import com.loohp.limbo.Utils.DataTypeIO;
|
||||
import com.loohp.limbo.Utils.NamespacedKey;
|
||||
|
||||
public class PacketLoginOutPluginMessaging extends PacketOut {
|
||||
|
||||
private int messageId;
|
||||
private NamespacedKey channel;
|
||||
private byte[] data;
|
||||
|
||||
public PacketLoginOutPluginMessaging(int messageId, NamespacedKey channel, byte[] data) {
|
||||
this.messageId = messageId;
|
||||
this.channel = channel;
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
public int getMessageId() {
|
||||
return messageId;
|
||||
}
|
||||
|
||||
public NamespacedKey getChannel() {
|
||||
return channel;
|
||||
}
|
||||
|
||||
public byte[] getData() {
|
||||
return data;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] serializePacket() throws IOException {
|
||||
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
|
||||
|
||||
DataOutputStream output = new DataOutputStream(buffer);
|
||||
output.writeByte(Packet.getLoginOut().get(getClass()));
|
||||
DataTypeIO.writeVarInt(output, messageId);
|
||||
DataTypeIO.writeString(output, channel.toString(), StandardCharsets.UTF_8);
|
||||
output.write(data);
|
||||
|
||||
return buffer.toByteArray();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package com.loohp.limbo.Server.Packets;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public abstract class PacketOut extends Packet {
|
||||
|
||||
public abstract byte[] serializePacket() throws IOException;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package com.loohp.limbo.Server.Packets;
|
||||
|
||||
import java.io.DataInputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
import com.loohp.limbo.Utils.DataTypeIO;
|
||||
|
||||
public class PacketPlayInChat extends PacketIn {
|
||||
|
||||
private String message;
|
||||
|
||||
public PacketPlayInChat(String message) {
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
public PacketPlayInChat(DataInputStream in) throws IOException {
|
||||
this(DataTypeIO.readString(in, StandardCharsets.UTF_8));
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package com.loohp.limbo.Server.Packets;
|
||||
|
||||
import java.io.DataInputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
public class PacketPlayInKeepAlive extends PacketIn {
|
||||
|
||||
long payload;
|
||||
|
||||
public PacketPlayInKeepAlive(long payload) {
|
||||
this.payload = payload;
|
||||
}
|
||||
|
||||
public PacketPlayInKeepAlive(DataInputStream in) throws IOException {
|
||||
this(in.readLong());
|
||||
}
|
||||
|
||||
public long getPayload() {
|
||||
return payload;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package com.loohp.limbo.Server.Packets;
|
||||
|
||||
import java.io.DataInputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
import com.loohp.limbo.Utils.DataTypeIO;
|
||||
import com.loohp.limbo.Utils.NamespacedKey;
|
||||
|
||||
public class PacketPlayInPluginMessaging extends PacketIn {
|
||||
|
||||
private NamespacedKey channel;
|
||||
private byte[] data;
|
||||
|
||||
public PacketPlayInPluginMessaging(NamespacedKey channel, byte[] data) {
|
||||
this.channel = channel;
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
public PacketPlayInPluginMessaging(DataInputStream in, int packetLength, int packetId) throws IOException {
|
||||
String rawChannel = DataTypeIO.readString(in, StandardCharsets.UTF_8);
|
||||
channel = new NamespacedKey(rawChannel);
|
||||
int dataLength = packetLength - DataTypeIO.getVarIntLength(packetId) - DataTypeIO.getStringLength(rawChannel, StandardCharsets.UTF_8);
|
||||
data = new byte[dataLength];
|
||||
in.read(data);
|
||||
}
|
||||
|
||||
public NamespacedKey getChannel() {
|
||||
return channel;
|
||||
}
|
||||
|
||||
public byte[] getData() {
|
||||
return data;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package com.loohp.limbo.Server.Packets;
|
||||
|
||||
import java.io.DataInputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
public class PacketPlayInPosition extends PacketIn {
|
||||
|
||||
private double x;
|
||||
private double y;
|
||||
private double z;
|
||||
private boolean onGround;
|
||||
|
||||
public PacketPlayInPosition(double x, double y, double z, boolean onGround) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.z = z;
|
||||
this.onGround = onGround;
|
||||
}
|
||||
|
||||
public PacketPlayInPosition(DataInputStream in) throws IOException {
|
||||
this(in.readDouble(), in.readDouble(), in.readDouble(), in.readBoolean());
|
||||
}
|
||||
|
||||
public double getX() {
|
||||
return x;
|
||||
}
|
||||
|
||||
public double getY() {
|
||||
return y;
|
||||
}
|
||||
|
||||
public double getZ() {
|
||||
return z;
|
||||
}
|
||||
|
||||
public boolean onGround() {
|
||||
return onGround;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
package com.loohp.limbo.Server.Packets;
|
||||
|
||||
import java.io.DataInputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
public class PacketPlayInPositionAndLook extends PacketIn {
|
||||
|
||||
private double x;
|
||||
private double y;
|
||||
private double z;
|
||||
private float yaw;
|
||||
private float pitch;
|
||||
private boolean onGround;
|
||||
|
||||
public PacketPlayInPositionAndLook(double x, double y, double z, float yaw, float pitch, boolean onGround) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.z = z;
|
||||
this.yaw = yaw;
|
||||
this.pitch = pitch;
|
||||
this.onGround = onGround;
|
||||
}
|
||||
|
||||
public PacketPlayInPositionAndLook(DataInputStream in) throws IOException {
|
||||
this(in.readDouble(), in.readDouble(), in.readDouble(), in.readFloat(), in.readFloat(), in.readBoolean());
|
||||
}
|
||||
|
||||
public double getX() {
|
||||
return x;
|
||||
}
|
||||
|
||||
public double getY() {
|
||||
return y;
|
||||
}
|
||||
|
||||
public double getZ() {
|
||||
return z;
|
||||
}
|
||||
|
||||
public float getYaw() {
|
||||
return yaw;
|
||||
}
|
||||
|
||||
public float getPitch() {
|
||||
return pitch;
|
||||
}
|
||||
|
||||
public boolean onGround() {
|
||||
return onGround;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package com.loohp.limbo.Server.Packets;
|
||||
|
||||
import java.io.DataInputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
public class PacketPlayInRotation extends PacketIn {
|
||||
|
||||
private float yaw;
|
||||
private float pitch;
|
||||
private boolean onGround;
|
||||
|
||||
public PacketPlayInRotation(float yaw, float pitch, boolean onGround) {
|
||||
this.yaw = yaw;
|
||||
this.pitch = pitch;
|
||||
this.onGround = onGround;
|
||||
}
|
||||
|
||||
public PacketPlayInRotation(DataInputStream in) throws IOException {
|
||||
this(in.readFloat(), in.readFloat(), in.readBoolean());
|
||||
}
|
||||
|
||||
public float getYaw() {
|
||||
return yaw;
|
||||
}
|
||||
|
||||
public float getPitch() {
|
||||
return pitch;
|
||||
}
|
||||
|
||||
public boolean onGround() {
|
||||
return onGround;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package com.loohp.limbo.Server.Packets;
|
||||
|
||||
import java.io.DataInputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
import com.loohp.limbo.Utils.DataTypeIO;
|
||||
|
||||
public class PacketPlayInTabComplete extends PacketIn {
|
||||
|
||||
private int id;
|
||||
private String text;
|
||||
|
||||
public PacketPlayInTabComplete(int id, String text) {
|
||||
this.id = id;
|
||||
this.text = text;
|
||||
}
|
||||
|
||||
public PacketPlayInTabComplete(DataInputStream in) throws IOException {
|
||||
this(DataTypeIO.readVarInt(in), DataTypeIO.readString(in, StandardCharsets.UTF_8));
|
||||
}
|
||||
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getText() {
|
||||
return text;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package com.loohp.limbo.Server.Packets;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.UUID;
|
||||
|
||||
import com.loohp.limbo.Utils.DataTypeIO;
|
||||
|
||||
public class PacketPlayOutChat extends PacketOut {
|
||||
|
||||
private String json;
|
||||
private int position;
|
||||
private UUID sender;
|
||||
|
||||
public PacketPlayOutChat(String json, int position, UUID sender) {
|
||||
this.json = json;
|
||||
this.position = position;
|
||||
this.sender = sender;
|
||||
}
|
||||
|
||||
public String getJson() {
|
||||
return json;
|
||||
}
|
||||
|
||||
public int getPosition() {
|
||||
return position;
|
||||
}
|
||||
|
||||
public UUID getSender() {
|
||||
return sender;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] serializePacket() throws IOException {
|
||||
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
|
||||
|
||||
DataOutputStream output = new DataOutputStream(buffer);
|
||||
output.writeByte(Packet.getPlayOut().get(getClass()));
|
||||
DataTypeIO.writeString(output, json, StandardCharsets.UTF_8);
|
||||
output.writeByte(position);
|
||||
DataTypeIO.writeUUID(output, sender);
|
||||
|
||||
return buffer.toByteArray();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package com.loohp.limbo.Server.Packets;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
public class PacketPlayOutDeclareCommands extends PacketOut {
|
||||
|
||||
private byte[] data;
|
||||
|
||||
public PacketPlayOutDeclareCommands(byte[] data) {
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
public byte[] getData() {
|
||||
return data;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] serializePacket() throws IOException {
|
||||
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
|
||||
|
||||
DataOutputStream output = new DataOutputStream(buffer);
|
||||
output.writeByte(Packet.getPlayOut().get(getClass()));
|
||||
output.write(data);
|
||||
|
||||
return buffer.toByteArray();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package com.loohp.limbo.Server.Packets;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
import com.loohp.limbo.Utils.DataTypeIO;
|
||||
|
||||
public class PacketPlayOutDisconnect extends PacketOut {
|
||||
|
||||
private String jsonReason;
|
||||
|
||||
public PacketPlayOutDisconnect(String jsonReason) {
|
||||
this.jsonReason = jsonReason;
|
||||
}
|
||||
|
||||
public String getJsonReason() {
|
||||
return jsonReason;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] serializePacket() throws IOException {
|
||||
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
|
||||
|
||||
DataOutputStream output = new DataOutputStream(buffer);
|
||||
output.writeByte(Packet.getPlayOut().get(getClass()));
|
||||
DataTypeIO.writeString(output, jsonReason, StandardCharsets.UTF_8);
|
||||
|
||||
return buffer.toByteArray();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package com.loohp.limbo.Server.Packets;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
public class PacketPlayOutGameState extends PacketOut {
|
||||
|
||||
private int reason;
|
||||
private float value;
|
||||
|
||||
public PacketPlayOutGameState(int reason, float value) {
|
||||
this.reason = reason;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public int getReason() {
|
||||
return reason;
|
||||
}
|
||||
|
||||
public float getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] serializePacket() throws IOException {
|
||||
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
|
||||
|
||||
DataOutputStream output = new DataOutputStream(buffer);
|
||||
output.writeByte(Packet.getPlayOut().get(getClass()));
|
||||
output.writeByte(reason);
|
||||
output.writeFloat(value);
|
||||
|
||||
return buffer.toByteArray();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package com.loohp.limbo.Server.Packets;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
public class PacketPlayOutKeepAlive extends PacketOut {
|
||||
|
||||
long payload;
|
||||
|
||||
public PacketPlayOutKeepAlive(long payload) {
|
||||
this.payload = payload;
|
||||
}
|
||||
|
||||
public long getPayload() {
|
||||
return payload;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] serializePacket() throws IOException {
|
||||
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
|
||||
|
||||
DataOutputStream output = new DataOutputStream(buffer);
|
||||
output.writeByte(Packet.getPlayOut().get(getClass()));
|
||||
output.writeLong(payload);
|
||||
|
||||
return buffer.toByteArray();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
package com.loohp.limbo.Server.Packets;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
import com.loohp.limbo.Utils.DataTypeIO;
|
||||
|
||||
public class PacketPlayOutLightUpdate extends PacketOut {
|
||||
|
||||
private int chunkX;
|
||||
private int chunkZ;
|
||||
private boolean trustEdges;
|
||||
private int skyLightBitMask;
|
||||
private int blockLightBitMask;
|
||||
private List<Byte[]> skylightArrays;
|
||||
private List<Byte[]> blocklightArrays;
|
||||
|
||||
public PacketPlayOutLightUpdate(int chunkX, int chunkZ, boolean trustEdges, List<Byte[]> skylightArrays, List<Byte[]> blocklightArrays) {
|
||||
this.chunkX = chunkX;
|
||||
this.chunkZ = chunkZ;
|
||||
this.trustEdges = trustEdges;
|
||||
this.skylightArrays = skylightArrays;
|
||||
this.blocklightArrays = blocklightArrays;
|
||||
|
||||
skyLightBitMask = 0;
|
||||
for (int i = Math.min(17, skylightArrays.size() - 1); i >= 0; i--) {
|
||||
skyLightBitMask = skyLightBitMask >> 1;
|
||||
if (skylightArrays.get(i) != null) {
|
||||
skyLightBitMask |= 131072;
|
||||
}
|
||||
}
|
||||
|
||||
blockLightBitMask = 0;
|
||||
for (int i = Math.min(17, blocklightArrays.size() - 1); i >= 0; i--) {
|
||||
blockLightBitMask = blockLightBitMask >> 1;
|
||||
if (blocklightArrays.get(i) != null) {
|
||||
blockLightBitMask |= 131072;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public int getChunkX() {
|
||||
return chunkX;
|
||||
}
|
||||
|
||||
public int getChunkZ() {
|
||||
return chunkZ;
|
||||
}
|
||||
|
||||
public boolean isTrustEdges() {
|
||||
return trustEdges;
|
||||
}
|
||||
|
||||
public int getSkyLightBitMask() {
|
||||
return skyLightBitMask;
|
||||
}
|
||||
|
||||
public int getBlockLightBitMask() {
|
||||
return blockLightBitMask;
|
||||
}
|
||||
|
||||
public List<Byte[]> getSkylightArrays() {
|
||||
return skylightArrays;
|
||||
}
|
||||
|
||||
public List<Byte[]> getBlocklightArrays() {
|
||||
return blocklightArrays;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] serializePacket() throws IOException {
|
||||
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
|
||||
|
||||
DataOutputStream output = new DataOutputStream(buffer);
|
||||
output.writeByte(Packet.getPlayOut().get(getClass()));
|
||||
DataTypeIO.writeVarInt(output, chunkX);
|
||||
DataTypeIO.writeVarInt(output, chunkZ);
|
||||
output.writeBoolean(trustEdges);
|
||||
DataTypeIO.writeVarInt(output, skyLightBitMask);
|
||||
DataTypeIO.writeVarInt(output, blockLightBitMask);
|
||||
DataTypeIO.writeVarInt(output, ~skyLightBitMask & 262143);
|
||||
DataTypeIO.writeVarInt(output, ~blockLightBitMask & 262143);
|
||||
|
||||
for (int i = skylightArrays.size() - 1; i >= 0; i--) {
|
||||
Byte[] array = skylightArrays.get(i);
|
||||
if (array != null) {
|
||||
DataTypeIO.writeVarInt(output, 2048);
|
||||
//System.out.println(Arrays.toString(ArrayUtils.toPrimitive(array)));
|
||||
for (int u = 0; u < array.length; u++) {
|
||||
output.writeByte(array[u]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = blocklightArrays.size() - 1; i >= 0; i--) {
|
||||
Byte[] array = blocklightArrays.get(i);
|
||||
if (array != null) {
|
||||
DataTypeIO.writeVarInt(output, 2048);
|
||||
//System.out.println(Arrays.toString(ArrayUtils.toPrimitive(array)));
|
||||
for (int u = 0; u < array.length; u++) {
|
||||
output.writeByte(array[u]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return buffer.toByteArray();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,146 @@
|
||||
package com.loohp.limbo.Server.Packets;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
import com.loohp.limbo.Utils.DataTypeIO;
|
||||
import com.loohp.limbo.Utils.GameMode;
|
||||
import com.loohp.limbo.Utils.NamespacedKey;
|
||||
import com.loohp.limbo.World.Environment;
|
||||
import com.loohp.limbo.World.World;
|
||||
|
||||
import net.querz.nbt.tag.CompoundTag;
|
||||
import net.querz.nbt.tag.ListTag;
|
||||
|
||||
public class PacketPlayOutLogin extends PacketOut {
|
||||
|
||||
private int entityId;
|
||||
private boolean isHardcore;
|
||||
private GameMode gamemode;
|
||||
private String[] worldsNames;
|
||||
private CompoundTag dimensionCodec;
|
||||
private Environment dimension;
|
||||
private String worldName;
|
||||
private long hashedSeed;
|
||||
private byte maxPlayers;
|
||||
private int viewDistance;
|
||||
private boolean reducedDebugInfo;
|
||||
private boolean enableRespawnScreen;
|
||||
private boolean isDebug;
|
||||
private boolean isFlat;
|
||||
|
||||
public PacketPlayOutLogin(int entityId, boolean isHardcore, GameMode gamemode,
|
||||
String[] worldsNames, CompoundTag dimensionCodec, World world, long hashedSeed,
|
||||
byte maxPlayers, int viewDistance, boolean reducedDebugInfo, boolean enableRespawnScreen, boolean isDebug,
|
||||
boolean isFlat) {
|
||||
this.entityId = entityId;
|
||||
this.isHardcore = isHardcore;
|
||||
this.gamemode = gamemode;
|
||||
this.worldsNames = worldsNames;
|
||||
this.dimensionCodec = dimensionCodec;
|
||||
this.dimension = world.getEnvironment();
|
||||
this.worldName = new NamespacedKey(world.getName()).toString();
|
||||
this.hashedSeed = hashedSeed;
|
||||
this.maxPlayers = maxPlayers;
|
||||
this.viewDistance = viewDistance;
|
||||
this.reducedDebugInfo = reducedDebugInfo;
|
||||
this.enableRespawnScreen = enableRespawnScreen;
|
||||
this.isDebug = isDebug;
|
||||
this.isFlat = isFlat;
|
||||
}
|
||||
|
||||
public int getEntityId() {
|
||||
return entityId;
|
||||
}
|
||||
|
||||
public boolean isHardcore() {
|
||||
return isHardcore;
|
||||
}
|
||||
|
||||
public GameMode getGamemode() {
|
||||
return gamemode;
|
||||
}
|
||||
|
||||
public String[] getWorldsNames() {
|
||||
return worldsNames;
|
||||
}
|
||||
|
||||
public CompoundTag getDimensionCodec() {
|
||||
return dimensionCodec;
|
||||
}
|
||||
|
||||
public Environment getDimension() {
|
||||
return dimension;
|
||||
}
|
||||
|
||||
public String getWorldName() {
|
||||
return worldName;
|
||||
}
|
||||
|
||||
public long getHashedSeed() {
|
||||
return hashedSeed;
|
||||
}
|
||||
|
||||
public byte getMaxPlayers() {
|
||||
return maxPlayers;
|
||||
}
|
||||
|
||||
public int getViewDistance() {
|
||||
return viewDistance;
|
||||
}
|
||||
|
||||
public boolean isReducedDebugInfo() {
|
||||
return reducedDebugInfo;
|
||||
}
|
||||
|
||||
public boolean isEnableRespawnScreen() {
|
||||
return enableRespawnScreen;
|
||||
}
|
||||
|
||||
public boolean isDebug() {
|
||||
return isDebug;
|
||||
}
|
||||
|
||||
public boolean isFlat() {
|
||||
return isFlat;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] serializePacket() throws IOException {
|
||||
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
|
||||
|
||||
DataOutputStream output = new DataOutputStream(buffer);
|
||||
output.writeByte(Packet.getPlayOut().get(getClass()));
|
||||
output.writeInt(entityId);
|
||||
output.writeBoolean(isHardcore);
|
||||
output.writeByte((byte) gamemode.getId());
|
||||
output.writeByte((byte) gamemode.getId());
|
||||
DataTypeIO.writeVarInt(output, worldsNames.length);
|
||||
for (int u = 0; u < worldsNames.length; u++) {
|
||||
DataTypeIO.writeString(output, worldsNames[u], StandardCharsets.UTF_8);
|
||||
}
|
||||
DataTypeIO.writeCompoundTag(output, dimensionCodec);
|
||||
CompoundTag tag = null;
|
||||
ListTag<CompoundTag> list = dimensionCodec.getCompoundTag("minecraft:dimension_type").getListTag("value").asCompoundTagList();
|
||||
for (CompoundTag each : list) {
|
||||
if (each.getString("name").equals(dimension.getNamespacedKey().toString())) {
|
||||
tag = each.getCompoundTag("element");
|
||||
break;
|
||||
}
|
||||
}
|
||||
DataTypeIO.writeCompoundTag(output, tag != null ? tag : list.get(0));
|
||||
DataTypeIO.writeString(output, worldName, StandardCharsets.UTF_8);
|
||||
output.writeLong(hashedSeed);
|
||||
DataTypeIO.writeVarInt(output, maxPlayers);
|
||||
DataTypeIO.writeVarInt(output, viewDistance);
|
||||
output.writeBoolean(reducedDebugInfo);
|
||||
output.writeBoolean(enableRespawnScreen);
|
||||
output.writeBoolean(isDebug);
|
||||
output.writeBoolean(isFlat);
|
||||
|
||||
return buffer.toByteArray();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,211 @@
|
||||
package com.loohp.limbo.Server.Packets;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.BitSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import com.loohp.limbo.Utils.BitsUtils;
|
||||
import com.loohp.limbo.Utils.DataTypeIO;
|
||||
import com.loohp.limbo.World.Environment;
|
||||
import com.loohp.limbo.World.GeneratedBlockDataMappings;
|
||||
|
||||
import net.querz.mca.Chunk;
|
||||
import net.querz.mca.Section;
|
||||
import net.querz.nbt.tag.CompoundTag;
|
||||
import net.querz.nbt.tag.ListTag;
|
||||
|
||||
public class PacketPlayOutMapChunk extends PacketOut {
|
||||
|
||||
private int chunkX;
|
||||
private int chunkZ;
|
||||
private Chunk chunk;
|
||||
private Environment environment;
|
||||
|
||||
public PacketPlayOutMapChunk(int chunkX, int chunkZ, Chunk chunk, Environment environment) {
|
||||
this.chunkX = chunkX;
|
||||
this.chunkZ = chunkZ;
|
||||
this.chunk = chunk;
|
||||
this.environment = environment;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public PacketPlayOutMapChunk(int chunkX, int chunkZ, Chunk chunk) {
|
||||
this(chunkZ, chunkZ, chunk, Environment.NORMAL);
|
||||
}
|
||||
|
||||
public Chunk getChunk() {
|
||||
return chunk;
|
||||
}
|
||||
|
||||
public int getChunkX() {
|
||||
return chunkX;
|
||||
}
|
||||
|
||||
public int getChunkZ() {
|
||||
return chunkZ;
|
||||
}
|
||||
|
||||
public Environment getEnvironment() {
|
||||
return environment;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] serializePacket() throws IOException {
|
||||
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
|
||||
|
||||
DataOutputStream output = new DataOutputStream(buffer);
|
||||
output.writeByte(Packet.getPlayOut().get(getClass()));
|
||||
|
||||
output.writeInt(chunkX);
|
||||
output.writeInt(chunkZ);
|
||||
output.writeBoolean(true);
|
||||
int bitmask = 0;
|
||||
for (int i = 0; i < 16; i++) {
|
||||
Section section = chunk.getSection(i);
|
||||
if (section != null) {
|
||||
bitmask = bitmask | (int) Math.pow(2, i);
|
||||
}
|
||||
}
|
||||
DataTypeIO.writeVarInt(output, bitmask);
|
||||
DataTypeIO.writeCompoundTag(output, chunk.getHeightMaps());
|
||||
|
||||
DataTypeIO.writeVarInt(output, 1024);
|
||||
int biome;
|
||||
if (environment.equals(Environment.END)) {
|
||||
biome = 9; //the_end
|
||||
} else if (environment.equals(Environment.NETHER)) {
|
||||
biome = 8; //nether_waste
|
||||
} else if (environment.equals(Environment.NORMAL)) {
|
||||
biome = 1; //plains
|
||||
} else {
|
||||
biome = 1; //plains
|
||||
}
|
||||
for (int i = 0; i < 1024; i++) {
|
||||
DataTypeIO.writeVarInt(output, biome);
|
||||
}
|
||||
|
||||
ByteArrayOutputStream dataBuffer = new ByteArrayOutputStream();
|
||||
DataOutputStream dataOut = new DataOutputStream(dataBuffer);
|
||||
for (int i = 0; i < 16; i++) {
|
||||
Section section = chunk.getSection(i);
|
||||
if (section != null) {
|
||||
int counter = 0;
|
||||
for (int x = 0; x < 16; x++) {
|
||||
for (int z = 0; z < 16; z++) {
|
||||
for (int y = 0; y < 16; y++) {
|
||||
CompoundTag tag = section.getBlockStateAt(x, y, z);
|
||||
if (tag != null && !tag.getString("Name").equals("minecraft:air")) {
|
||||
counter++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
dataOut.writeShort(counter);
|
||||
|
||||
int newBits = 32 - Integer.numberOfLeadingZeros(section.getPalette().size() - 1);
|
||||
newBits = Math.max(newBits, 4);
|
||||
//Limbo.getInstance().getConsole().sendMessage(i + " " + newBits);
|
||||
if (newBits <= 8) {
|
||||
/*
|
||||
if (newBits == 4) {
|
||||
dataOut.writeByte(4);
|
||||
} else {
|
||||
newBits = 8;
|
||||
ChunkDataUtils.adjustBlockStateBits(newBits, section, chunk.getDataVersion());
|
||||
dataOut.writeByte(8);
|
||||
}
|
||||
*/
|
||||
dataOut.writeByte(newBits);
|
||||
|
||||
DataTypeIO.writeVarInt(dataOut, section.getPalette().size());
|
||||
//Limbo.getInstance().getConsole().sendMessage(section.getPalette().size());
|
||||
Iterator<CompoundTag> itr1 = section.getPalette().iterator();
|
||||
//Limbo.getInstance().getConsole().sendMessage("Nonnull -> " + i + " " + newBits);
|
||||
while (itr1.hasNext()) {
|
||||
CompoundTag tag = itr1.next();
|
||||
DataTypeIO.writeVarInt(dataOut, GeneratedBlockDataMappings.getGlobalPaletteIDFromState(tag));
|
||||
//Limbo.getInstance().getConsole().sendMessage(tag + " -> " + GeneratedDataUtils.getGlobalPaletteIDFromState(tag));
|
||||
}
|
||||
|
||||
BitSet bits = BitSet.valueOf(section.getBlockStates());
|
||||
int shift = 64 % newBits;
|
||||
int longsNeeded = (int) Math.ceil(4096 / (double) (64 / newBits));
|
||||
for (int u = 64; u <= bits.length(); u += 64) {
|
||||
bits = BitsUtils.shiftAfter(bits, u - shift, shift);
|
||||
}
|
||||
|
||||
long[] formattedLongs = bits.toLongArray();
|
||||
//Limbo.getInstance().getConsole().sendMessage(longsNeeded + "");
|
||||
|
||||
DataTypeIO.writeVarInt(dataOut, longsNeeded);
|
||||
for (int u = 0; u < longsNeeded; u++) {
|
||||
if (u < formattedLongs.length) {
|
||||
dataOut.writeLong(formattedLongs[u]);
|
||||
} else {
|
||||
dataOut.writeLong(0);
|
||||
}
|
||||
//Limbo.getInstance().getConsole().sendMessage(Arrays.toString(section.getBlockStates()));
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
dataOut.writeByte(15);
|
||||
section.getBlockStates();
|
||||
int longsNeeded = 1024;
|
||||
List<Integer> list = new LinkedList<>();
|
||||
for (int y = 0; y < 16; y++) {
|
||||
for (int z = 0; z < 16; z++) {
|
||||
for (int x = 0; x < 16; x++) {
|
||||
list.add(GeneratedBlockDataMappings.getGlobalPaletteIDFromState(section.getBlockStateAt(x, y, z)));
|
||||
}
|
||||
}
|
||||
}
|
||||
List<Long> globalLongs = new ArrayList<>();
|
||||
long currentLong = 0;
|
||||
int pos = 0;
|
||||
int u = 0;
|
||||
while (pos < longsNeeded) {
|
||||
if (u == 3) {
|
||||
globalLongs.add(currentLong);
|
||||
currentLong = 0;
|
||||
u = 0;
|
||||
pos++;
|
||||
} else {
|
||||
u++;
|
||||
}
|
||||
int id = list.isEmpty() ? 0 : list.remove(0);
|
||||
currentLong = currentLong << 15;
|
||||
currentLong |= (long) id;
|
||||
}
|
||||
DataTypeIO.writeVarInt(dataOut, longsNeeded);
|
||||
for (int j = 0; j < longsNeeded; j++) {
|
||||
if (j < globalLongs.size()) {
|
||||
dataOut.writeLong(globalLongs.get(j));
|
||||
} else {
|
||||
dataOut.writeLong(0);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
byte[] data = dataBuffer.toByteArray();
|
||||
DataTypeIO.writeVarInt(output, data.length);
|
||||
output.write(data);
|
||||
|
||||
ListTag<CompoundTag> tileEntities = chunk.getTileEntities();
|
||||
DataTypeIO.writeVarInt(output, tileEntities.size());
|
||||
for (CompoundTag each : tileEntities) {
|
||||
DataTypeIO.writeCompoundTag(output, each);
|
||||
}
|
||||
|
||||
return buffer.toByteArray();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
package com.loohp.limbo.Server.Packets;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
public class PacketPlayOutPlayerAbilities extends PacketOut {
|
||||
|
||||
public enum PlayerAbilityFlags {
|
||||
INVULNERABLE(0x01),
|
||||
FLY(0x02),
|
||||
ALLOW_FLYING(0x04),
|
||||
CREATIVE(0x08);
|
||||
|
||||
int bitvalue;
|
||||
|
||||
PlayerAbilityFlags(int bitvalue) {
|
||||
this.bitvalue = bitvalue;
|
||||
}
|
||||
|
||||
public int getValue() {
|
||||
return bitvalue;
|
||||
}
|
||||
}
|
||||
|
||||
private PlayerAbilityFlags[] flags;
|
||||
private float flySpeed;
|
||||
private float fieldOfField;
|
||||
|
||||
public PacketPlayOutPlayerAbilities(float flySpeed, float fieldOfField, PlayerAbilityFlags... flags) {
|
||||
this.flags = flags;
|
||||
this.flySpeed = flySpeed;
|
||||
this.fieldOfField = fieldOfField;
|
||||
}
|
||||
|
||||
public PlayerAbilityFlags[] getFlags() {
|
||||
return flags;
|
||||
}
|
||||
|
||||
public float getFlySpeed() {
|
||||
return flySpeed;
|
||||
}
|
||||
|
||||
public float getFieldOfField() {
|
||||
return fieldOfField;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] serializePacket() throws IOException {
|
||||
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
|
||||
|
||||
DataOutputStream output = new DataOutputStream(buffer);
|
||||
output.writeByte(Packet.getPlayOut().get(getClass()));
|
||||
int value = 0;
|
||||
for (PlayerAbilityFlags flag : flags) {
|
||||
value = value | flag.getValue();
|
||||
}
|
||||
|
||||
output.writeByte(value);
|
||||
output.writeFloat(flySpeed);
|
||||
output.writeFloat(fieldOfField);
|
||||
|
||||
return buffer.toByteArray();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,168 @@
|
||||
package com.loohp.limbo.Server.Packets;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
import com.loohp.limbo.Server.Packets.PacketPlayOutPlayerInfo.PlayerInfoData.PlayerInfoDataAddPlayer;
|
||||
import com.loohp.limbo.Utils.DataTypeIO;
|
||||
import com.loohp.limbo.Utils.GameMode;
|
||||
|
||||
public class PacketPlayOutPlayerInfo extends PacketOut {
|
||||
|
||||
public enum PlayerInfoAction {
|
||||
ADD_PLAYER(0), UPDATE_GAMEMODE(1), UPDATE_LATENCY(2), UPDATE_DISPLAY_NAME(3), REMOVE_PLAYER(4);
|
||||
|
||||
int id;
|
||||
|
||||
PlayerInfoAction(int id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
}
|
||||
|
||||
private PlayerInfoAction action;
|
||||
private UUID uuid;
|
||||
private PlayerInfoData data;
|
||||
|
||||
public PacketPlayOutPlayerInfo(PlayerInfoAction action, UUID uuid, PlayerInfoData data) {
|
||||
this.action = action;
|
||||
this.uuid = uuid;
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
public PlayerInfoAction getAction() {
|
||||
return action;
|
||||
}
|
||||
|
||||
public UUID getUuid() {
|
||||
return uuid;
|
||||
}
|
||||
|
||||
public PlayerInfoData getData() {
|
||||
return data;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] serializePacket() throws IOException {
|
||||
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
|
||||
|
||||
DataOutputStream output = new DataOutputStream(buffer);
|
||||
output.writeByte(Packet.getPlayOut().get(getClass()));
|
||||
DataTypeIO.writeVarInt(output, action.getId());
|
||||
DataTypeIO.writeVarInt(output, 1);
|
||||
DataTypeIO.writeUUID(output, uuid);
|
||||
|
||||
switch (action) {
|
||||
case ADD_PLAYER:
|
||||
PlayerInfoDataAddPlayer data = (PlayerInfoDataAddPlayer) this.data;
|
||||
DataTypeIO.writeString(output, data.getName(), StandardCharsets.UTF_8);
|
||||
if (data.getProperty().isPresent()) {
|
||||
DataTypeIO.writeVarInt(output, 1);
|
||||
DataTypeIO.writeString(output, "textures", StandardCharsets.UTF_8);
|
||||
DataTypeIO.writeString(output, data.getProperty().get().getSkin(), StandardCharsets.UTF_8);
|
||||
output.writeBoolean(true);
|
||||
DataTypeIO.writeString(output, data.getProperty().get().getSignature(), StandardCharsets.UTF_8);
|
||||
} else {
|
||||
DataTypeIO.writeVarInt(output, 0);
|
||||
}
|
||||
DataTypeIO.writeVarInt(output, data.getGamemode().getId());
|
||||
DataTypeIO.writeVarInt(output, data.getPing());
|
||||
if (data.getDisplayNameJson().isPresent()) {
|
||||
output.writeBoolean(true);
|
||||
DataTypeIO.writeString(output, data.getDisplayNameJson().get(), StandardCharsets.UTF_8);
|
||||
} else {
|
||||
output.writeBoolean(false);
|
||||
}
|
||||
break;
|
||||
case REMOVE_PLAYER:
|
||||
break;
|
||||
case UPDATE_DISPLAY_NAME:
|
||||
break;
|
||||
case UPDATE_GAMEMODE:
|
||||
break;
|
||||
case UPDATE_LATENCY:
|
||||
break;
|
||||
}
|
||||
|
||||
return buffer.toByteArray();
|
||||
}
|
||||
|
||||
// =========
|
||||
|
||||
public static class PlayerInfoData {
|
||||
|
||||
public static class PlayerInfoDataAddPlayer extends PlayerInfoData {
|
||||
|
||||
private String name;
|
||||
private Optional<PlayerSkinProperty> skin;
|
||||
private GameMode gamemode;
|
||||
private int ping;
|
||||
private boolean hasDisplayName;
|
||||
private Optional<String> displayNameJson;
|
||||
|
||||
public PlayerInfoDataAddPlayer(String name, Optional<PlayerSkinProperty> skin, GameMode gamemode, int ping,
|
||||
boolean hasDisplayName, Optional<String> displayNameJson) {
|
||||
this.name = name;
|
||||
this.skin = skin;
|
||||
this.gamemode = gamemode;
|
||||
this.ping = ping;
|
||||
this.hasDisplayName = hasDisplayName;
|
||||
this.displayNameJson = displayNameJson;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public Optional<PlayerSkinProperty> getProperty() {
|
||||
return skin;
|
||||
}
|
||||
|
||||
public GameMode getGamemode() {
|
||||
return gamemode;
|
||||
}
|
||||
|
||||
public int getPing() {
|
||||
return ping;
|
||||
}
|
||||
|
||||
public boolean isHasDisplayName() {
|
||||
return hasDisplayName;
|
||||
}
|
||||
|
||||
public Optional<String> getDisplayNameJson() {
|
||||
return displayNameJson;
|
||||
}
|
||||
|
||||
public static class PlayerSkinProperty {
|
||||
|
||||
private String skin;
|
||||
private String signature;
|
||||
|
||||
public PlayerSkinProperty(String skin, String signature) {
|
||||
this.skin = skin;
|
||||
this.signature = signature;
|
||||
}
|
||||
|
||||
public String getSkin() {
|
||||
return skin;
|
||||
}
|
||||
|
||||
public String getSignature() {
|
||||
return signature;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package com.loohp.limbo.Server.Packets;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
import com.loohp.limbo.Utils.DataTypeIO;
|
||||
import com.loohp.limbo.Utils.NamespacedKey;
|
||||
|
||||
public class PacketPlayOutPluginMessaging extends PacketOut {
|
||||
|
||||
private NamespacedKey channel;
|
||||
private byte[] data;
|
||||
|
||||
public PacketPlayOutPluginMessaging(NamespacedKey channel, byte[] data) {
|
||||
this.channel = channel;
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
public NamespacedKey getChannel() {
|
||||
return channel;
|
||||
}
|
||||
|
||||
public byte[] getData() {
|
||||
return data;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] serializePacket() throws IOException {
|
||||
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
|
||||
|
||||
DataOutputStream output = new DataOutputStream(buffer);
|
||||
output.writeByte(Packet.getPlayOut().get(getClass()));
|
||||
DataTypeIO.writeString(output, channel.toString(), StandardCharsets.UTF_8);
|
||||
output.write(data);
|
||||
|
||||
return buffer.toByteArray();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
package com.loohp.limbo.Server.Packets;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import com.loohp.limbo.Utils.DataTypeIO;
|
||||
|
||||
public class PacketPlayOutPositionAndLook extends PacketOut {
|
||||
|
||||
public enum PlayerTeleportFlags {
|
||||
X((byte) 0x01),
|
||||
Y((byte) 0x02),
|
||||
Z((byte) 0x04),
|
||||
Y_ROT((byte) 0x08),
|
||||
X_ROT((byte) 0x10);
|
||||
|
||||
byte bit;
|
||||
|
||||
PlayerTeleportFlags(byte bit) {
|
||||
this.bit = bit;
|
||||
}
|
||||
|
||||
public byte getBit() {
|
||||
return bit;
|
||||
}
|
||||
}
|
||||
|
||||
private double x;
|
||||
private double y;
|
||||
private double z;
|
||||
private float yaw;
|
||||
private float pitch;
|
||||
private Set<PlayerTeleportFlags> flags;
|
||||
private int teleportId;
|
||||
|
||||
public PacketPlayOutPositionAndLook(double x, double y, double z, float yaw, float pitch, int teleportId, PlayerTeleportFlags... flags) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.z = z;
|
||||
this.yaw = yaw;
|
||||
this.pitch = pitch;
|
||||
this.teleportId = teleportId;
|
||||
this.flags = Arrays.asList(flags).stream().collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
public double getX() {
|
||||
return x;
|
||||
}
|
||||
|
||||
public double getY() {
|
||||
return y;
|
||||
}
|
||||
|
||||
public double getZ() {
|
||||
return z;
|
||||
}
|
||||
|
||||
public float getYaw() {
|
||||
return yaw;
|
||||
}
|
||||
|
||||
public float getPitch() {
|
||||
return pitch;
|
||||
}
|
||||
|
||||
public Set<PlayerTeleportFlags> getFlags() {
|
||||
return flags;
|
||||
}
|
||||
|
||||
public int getTeleportId() {
|
||||
return teleportId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] serializePacket() throws IOException {
|
||||
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
|
||||
|
||||
DataOutputStream output = new DataOutputStream(buffer);
|
||||
output.writeByte(Packet.getPlayOut().get(getClass()));
|
||||
output.writeDouble(x);
|
||||
output.writeDouble(y);
|
||||
output.writeDouble(z);
|
||||
output.writeFloat(yaw);
|
||||
output.writeFloat(pitch);
|
||||
|
||||
byte flag = 0;
|
||||
for (PlayerTeleportFlags each : flags) {
|
||||
flag = (byte) (flag | each.getBit());
|
||||
}
|
||||
|
||||
output.writeByte(flag);
|
||||
DataTypeIO.writeVarInt(output, teleportId);
|
||||
|
||||
return buffer.toByteArray();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
package com.loohp.limbo.Server.Packets;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
import com.loohp.limbo.Utils.DataTypeIO;
|
||||
import com.loohp.limbo.Utils.GameMode;
|
||||
import com.loohp.limbo.Utils.NamespacedKey;
|
||||
import com.loohp.limbo.World.Environment;
|
||||
import com.loohp.limbo.World.World;
|
||||
|
||||
import net.querz.nbt.tag.CompoundTag;
|
||||
import net.querz.nbt.tag.ListTag;
|
||||
|
||||
public class PacketPlayOutRespawn extends PacketOut {
|
||||
|
||||
private Environment dimension;
|
||||
private String worldName;
|
||||
private CompoundTag dimensionCodec;
|
||||
private long hashedSeed;
|
||||
private GameMode gamemode;
|
||||
private boolean isDebug;
|
||||
private boolean isFlat;
|
||||
private boolean copyMetaData;
|
||||
|
||||
public PacketPlayOutRespawn(World world, CompoundTag dimensionCodec, long hashedSeed, GameMode gamemode, boolean isDebug,
|
||||
boolean isFlat, boolean copyMetaData) {
|
||||
this.dimension = world.getEnvironment();
|
||||
this.dimensionCodec = dimensionCodec;
|
||||
this.worldName = new NamespacedKey(world.getName()).toString();
|
||||
this.hashedSeed = hashedSeed;
|
||||
this.gamemode = gamemode;
|
||||
this.isDebug = isDebug;
|
||||
this.isFlat = isFlat;
|
||||
this.copyMetaData = copyMetaData;
|
||||
}
|
||||
|
||||
public CompoundTag getDimensionCodec() {
|
||||
return dimensionCodec;
|
||||
}
|
||||
|
||||
public Environment getDimension() {
|
||||
return dimension;
|
||||
}
|
||||
|
||||
public String getWorldName() {
|
||||
return worldName;
|
||||
}
|
||||
|
||||
public long getHashedSeed() {
|
||||
return hashedSeed;
|
||||
}
|
||||
|
||||
public GameMode getGamemode() {
|
||||
return gamemode;
|
||||
}
|
||||
|
||||
public boolean isDebug() {
|
||||
return isDebug;
|
||||
}
|
||||
|
||||
public boolean isFlat() {
|
||||
return isFlat;
|
||||
}
|
||||
|
||||
public boolean isCopyMetaData() {
|
||||
return copyMetaData;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] serializePacket() throws IOException {
|
||||
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
|
||||
|
||||
DataOutputStream output = new DataOutputStream(buffer);
|
||||
output.writeByte(Packet.getPlayOut().get(getClass()));
|
||||
CompoundTag tag = null;
|
||||
ListTag<CompoundTag> list = dimensionCodec.getCompoundTag("minecraft:dimension_type").getListTag("value").asCompoundTagList();
|
||||
for (CompoundTag each : list) {
|
||||
if (each.getString("name").equals(dimension.getNamespacedKey().toString())) {
|
||||
tag = each.getCompoundTag("element");
|
||||
break;
|
||||
}
|
||||
}
|
||||
DataTypeIO.writeCompoundTag(output, tag != null ? tag : list.get(0));
|
||||
DataTypeIO.writeString(output, worldName, StandardCharsets.UTF_8);
|
||||
output.writeLong(hashedSeed);
|
||||
output.writeByte((byte) gamemode.getId());
|
||||
output.writeByte((byte) gamemode.getId());
|
||||
output.writeBoolean(isDebug);
|
||||
output.writeBoolean(isFlat);
|
||||
output.writeBoolean(copyMetaData);
|
||||
|
||||
return buffer.toByteArray();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package com.loohp.limbo.Server.Packets;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
import com.loohp.limbo.Utils.DataTypeIO;
|
||||
|
||||
public class PacketPlayOutShowPlayerSkins extends PacketOut {
|
||||
|
||||
private int entityId;
|
||||
|
||||
public PacketPlayOutShowPlayerSkins(int entityId) {
|
||||
this.entityId = entityId;
|
||||
}
|
||||
|
||||
public int getEntityId() {
|
||||
return entityId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] serializePacket() throws IOException {
|
||||
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
|
||||
|
||||
DataOutputStream output = new DataOutputStream(buffer);
|
||||
output.writeByte(Packet.getPlayOut().get(getClass()));
|
||||
|
||||
DataTypeIO.writeVarInt(output, entityId);
|
||||
output.writeByte(16);
|
||||
DataTypeIO.writeVarInt(output, 0);
|
||||
int bitmask = 0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40;
|
||||
output.writeByte(bitmask);
|
||||
output.writeByte(0xff);
|
||||
|
||||
return buffer.toByteArray();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package com.loohp.limbo.Server.Packets;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
import com.loohp.limbo.Utils.DataTypeIO;
|
||||
import com.loohp.limbo.World.BlockPosition;
|
||||
|
||||
public class PacketPlayOutSpawnPosition extends PacketOut {
|
||||
|
||||
private BlockPosition position;
|
||||
|
||||
public PacketPlayOutSpawnPosition(BlockPosition position) {
|
||||
this.position = position;
|
||||
}
|
||||
|
||||
public BlockPosition getPosition() {
|
||||
return position;
|
||||
}
|
||||
|
||||
public byte[] serializePacket() throws IOException {
|
||||
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
|
||||
|
||||
DataOutputStream output = new DataOutputStream(buffer);
|
||||
output.writeByte(Packet.getPlayOut().get(getClass()));
|
||||
DataTypeIO.writeBlockPosition(output, position);
|
||||
|
||||
return buffer.toByteArray();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
package com.loohp.limbo.Server.Packets;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Optional;
|
||||
|
||||
import com.loohp.limbo.Utils.DataTypeIO;
|
||||
|
||||
import net.md_5.bungee.api.chat.BaseComponent;
|
||||
import net.md_5.bungee.chat.ComponentSerializer;
|
||||
|
||||
public class PacketPlayOutTabComplete extends PacketOut {
|
||||
|
||||
private int id;
|
||||
private int start;
|
||||
private int length;
|
||||
private TabCompleteMatches[] matches;
|
||||
|
||||
public PacketPlayOutTabComplete(int id, int start, int length, TabCompleteMatches... matches) {
|
||||
this.id = id;
|
||||
this.start = start;
|
||||
this.length = length;
|
||||
this.matches = matches;
|
||||
}
|
||||
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public int getStart() {
|
||||
return start;
|
||||
}
|
||||
|
||||
public int getLength() {
|
||||
return length;
|
||||
}
|
||||
|
||||
public TabCompleteMatches[] getMatches() {
|
||||
return matches;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] serializePacket() throws IOException {
|
||||
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
|
||||
|
||||
DataOutputStream output = new DataOutputStream(buffer);
|
||||
output.writeByte(Packet.getPlayOut().get(getClass()));
|
||||
DataTypeIO.writeVarInt(output, id);
|
||||
DataTypeIO.writeVarInt(output, start);
|
||||
DataTypeIO.writeVarInt(output, length);
|
||||
DataTypeIO.writeVarInt(output, matches.length);
|
||||
|
||||
for (TabCompleteMatches match : matches) {
|
||||
DataTypeIO.writeString(output, match.getMatch(), StandardCharsets.UTF_8);
|
||||
if (match.getTooltip().isPresent()) {
|
||||
output.writeBoolean(true);
|
||||
DataTypeIO.writeString(output, ComponentSerializer.toString(match.getTooltip().get()), StandardCharsets.UTF_8);
|
||||
} else {
|
||||
output.writeBoolean(false);
|
||||
}
|
||||
}
|
||||
|
||||
return buffer.toByteArray();
|
||||
}
|
||||
|
||||
public static class TabCompleteMatches {
|
||||
|
||||
private String match;
|
||||
private Optional<BaseComponent[]> tooltip;
|
||||
|
||||
public TabCompleteMatches(String match, BaseComponent... tooltip) {
|
||||
this.match = match;
|
||||
this.tooltip = tooltip.length > 0 ? Optional.of(tooltip) : Optional.empty();
|
||||
}
|
||||
|
||||
public String getMatch() {
|
||||
return match;
|
||||
}
|
||||
|
||||
public Optional<BaseComponent[]> getTooltip() {
|
||||
return tooltip;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package com.loohp.limbo.Server.Packets;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
import com.loohp.limbo.Utils.DataTypeIO;
|
||||
|
||||
public class PacketPlayOutUpdateViewPosition extends PacketOut {
|
||||
|
||||
private int chunkX;
|
||||
private int chunkZ;
|
||||
|
||||
public PacketPlayOutUpdateViewPosition(int chunkX, int chunkZ) {
|
||||
this.chunkX = chunkX;
|
||||
this.chunkZ = chunkZ;
|
||||
}
|
||||
|
||||
public int getChunkX() {
|
||||
return chunkX;
|
||||
}
|
||||
|
||||
public int getChunkZ() {
|
||||
return chunkZ;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] serializePacket() throws IOException {
|
||||
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
|
||||
|
||||
DataOutputStream output = new DataOutputStream(buffer);
|
||||
output.writeByte(Packet.getPlayOut().get(getClass()));
|
||||
DataTypeIO.writeVarInt(output, chunkX);
|
||||
DataTypeIO.writeVarInt(output, chunkZ);
|
||||
|
||||
return buffer.toByteArray();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package com.loohp.limbo.Server.Packets;
|
||||
|
||||
import java.io.DataInputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
public class PacketStatusInPing extends PacketIn {
|
||||
|
||||
private long payload;
|
||||
|
||||
public PacketStatusInPing(long payload) {
|
||||
this.payload = payload;
|
||||
}
|
||||
|
||||
public PacketStatusInPing(DataInputStream in) throws IOException {
|
||||
this(in.readLong());
|
||||
}
|
||||
|
||||
public long getPayload() {
|
||||
return payload;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package com.loohp.limbo.Server.Packets;
|
||||
|
||||
import java.io.DataInputStream;
|
||||
|
||||
public class PacketStatusInRequest extends PacketIn {
|
||||
|
||||
public PacketStatusInRequest() {
|
||||
|
||||
}
|
||||
|
||||
public PacketStatusInRequest(DataInputStream in) {
|
||||
this();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package com.loohp.limbo.Server.Packets;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
public class PacketStatusOutPong extends PacketOut {
|
||||
|
||||
private long payload;
|
||||
|
||||
public PacketStatusOutPong(long payload) {
|
||||
this.payload = payload;
|
||||
}
|
||||
|
||||
public long getPayload() {
|
||||
return payload;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] serializePacket() throws IOException {
|
||||
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
|
||||
|
||||
DataOutputStream output = new DataOutputStream(buffer);
|
||||
output.writeByte(Packet.getStatusOut().get(getClass()));
|
||||
output.writeLong(payload);
|
||||
|
||||
return buffer.toByteArray();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package com.loohp.limbo.Server.Packets;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
import com.loohp.limbo.Utils.DataTypeIO;
|
||||
|
||||
public class PacketStatusOutResponse extends PacketOut {
|
||||
|
||||
private String json;
|
||||
|
||||
public PacketStatusOutResponse(String json) {
|
||||
this.json = json;
|
||||
}
|
||||
|
||||
public String getJson() {
|
||||
return json;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] serializePacket() throws IOException {
|
||||
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
|
||||
|
||||
DataOutputStream output = new DataOutputStream(buffer);
|
||||
output.writeByte(Packet.getStatusOut().get(getClass()));
|
||||
DataTypeIO.writeString(output, json, StandardCharsets.UTF_8);
|
||||
|
||||
return buffer.toByteArray();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
package com.loohp.limbo.Server;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.InetAddress;
|
||||
import java.net.ServerSocket;
|
||||
import java.net.Socket;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import com.loohp.limbo.Limbo;
|
||||
|
||||
public class ServerConnection extends Thread {
|
||||
|
||||
private ServerSocket serverSocket;
|
||||
private List<ClientConnection> clients;
|
||||
private String ip;
|
||||
private int port;
|
||||
private KeepAliveSender keepAliveSender;
|
||||
|
||||
public ServerConnection(String ip, int port) {
|
||||
clients = new ArrayList<ClientConnection>();
|
||||
this.ip = ip;
|
||||
this.port = port;
|
||||
start();
|
||||
keepAliveSender = new KeepAliveSender();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
serverSocket = new ServerSocket(port, 50, InetAddress.getByName(ip));
|
||||
Limbo.getInstance().getConsole().sendMessage("Limbo server listening on /" + serverSocket.getInetAddress().getHostName() + ":" + serverSocket.getLocalPort());
|
||||
while (true) {
|
||||
Socket connection = serverSocket.accept();
|
||||
//String str = connection.getInetAddress().getHostName() + ":" + connection.getPort();
|
||||
//Limbo.getInstance().getConsole().sendMessage("[/127.0.0.1:57310] <-> InitialHandler has pinged);
|
||||
ClientConnection sc = new ClientConnection(connection);
|
||||
clients.add(sc);
|
||||
sc.start();
|
||||
}
|
||||
} catch(IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public KeepAliveSender getKeepAliveSender() {
|
||||
return keepAliveSender;
|
||||
}
|
||||
|
||||
public ServerSocket getServerSocket() {
|
||||
return serverSocket;
|
||||
}
|
||||
|
||||
public List<ClientConnection> getClients() {
|
||||
return clients;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package com.loohp.limbo;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
|
||||
import com.loohp.limbo.Player.Player;
|
||||
import com.loohp.limbo.Utils.GameMode;
|
||||
|
||||
@Deprecated
|
||||
public class Unsafe {
|
||||
|
||||
private com.loohp.limbo.Player.Unsafe playerUnsafe;
|
||||
|
||||
protected Unsafe() {
|
||||
try {
|
||||
Constructor<com.loohp.limbo.Player.Unsafe> playerConstructor = com.loohp.limbo.Player.Unsafe.class.getDeclaredConstructor();
|
||||
playerConstructor.setAccessible(true);
|
||||
playerUnsafe = playerConstructor.newInstance();
|
||||
playerConstructor.setAccessible(false);
|
||||
} catch (Exception e) {e.printStackTrace();}
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public void setPlayerGameModeSilently(Player player, GameMode mode) {
|
||||
playerUnsafe.a(player, mode);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public void setPlayerEntityId(Player player, int entityId) {
|
||||
playerUnsafe.a(player, entityId);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package com.loohp.limbo.Utils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.BitSet;
|
||||
import java.util.List;
|
||||
|
||||
public class BitsUtils {
|
||||
|
||||
public static BitSet shiftAfter(BitSet bitset, int from, int shift) {
|
||||
BitSet subset = bitset.get(from, bitset.length());
|
||||
for (int i = 0; i < subset.length(); i++) {
|
||||
bitset.set(from + shift + i, subset.get(i));
|
||||
}
|
||||
if (shift > 0) {
|
||||
for (int i = 0; i < shift; i++) {
|
||||
bitset.set(from + i, false);
|
||||
}
|
||||
}
|
||||
return bitset;
|
||||
}
|
||||
|
||||
public static String toLongString(BitSet bitset) {
|
||||
List<String> list = new ArrayList<>();
|
||||
for (long l : bitset.toLongArray()) {
|
||||
list.add(Long.toBinaryString(l));
|
||||
}
|
||||
return Arrays.toString(list.toArray());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
package com.loohp.limbo.Utils;
|
||||
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.net.InetAddress;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.UUID;
|
||||
|
||||
import com.google.common.io.ByteArrayDataInput;
|
||||
import com.google.common.io.ByteArrayDataOutput;
|
||||
import com.google.common.io.ByteStreams;
|
||||
import com.loohp.limbo.Server.Packets.PacketPlayOutPluginMessaging;
|
||||
|
||||
public class BungeeLoginMessageUtils {
|
||||
|
||||
public static void sendUUIDRequest(DataOutputStream output) throws IOException {
|
||||
ByteArrayDataOutput out = ByteStreams.newDataOutput();
|
||||
out.writeUTF("UUID");
|
||||
|
||||
PacketPlayOutPluginMessaging packet = new PacketPlayOutPluginMessaging(new NamespacedKey("bungeecord", "main"), out.toByteArray());
|
||||
byte[] packetByte = packet.serializePacket();
|
||||
DataTypeIO.writeVarInt(output, packetByte.length);
|
||||
output.write(packetByte);
|
||||
}
|
||||
|
||||
public static UUID readUUIDResponse(byte[] data) {
|
||||
ByteArrayDataInput in = ByteStreams.newDataInput(data);
|
||||
String subchannel = in.readUTF();
|
||||
if (subchannel.equals("UUID")) {
|
||||
return UUID.fromString(in.readUTF());
|
||||
} else {
|
||||
throw new RuntimeException("Bungeecord Message receieved is not an IP");
|
||||
}
|
||||
}
|
||||
|
||||
public static void sendIPRequest(DataOutputStream output) throws IOException {
|
||||
ByteArrayDataOutput out = ByteStreams.newDataOutput();
|
||||
out.writeUTF("IP");
|
||||
|
||||
PacketPlayOutPluginMessaging packet = new PacketPlayOutPluginMessaging(new NamespacedKey("bungeecord", "main"), out.toByteArray());
|
||||
byte[] packetByte = packet.serializePacket();
|
||||
DataTypeIO.writeVarInt(output, packetByte.length);
|
||||
output.write(packetByte);
|
||||
}
|
||||
|
||||
public static InetAddress readIPResponse(byte[] data) throws UnknownHostException {
|
||||
ByteArrayDataInput in = ByteStreams.newDataInput(data);
|
||||
String subchannel = in.readUTF();
|
||||
if (subchannel.equals("IP")) {
|
||||
String ip = in.readUTF();
|
||||
in.readInt();
|
||||
return InetAddress.getByName(ip);
|
||||
} else {
|
||||
throw new RuntimeException("Bungeecord Message receieved is not an IP");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package com.loohp.limbo.Utils;
|
||||
|
||||
import net.querz.mca.Section;
|
||||
|
||||
public class ChunkDataUtils {
|
||||
|
||||
public static void adjustBlockStateBits(int newBits, Section section, int dataVersion) {
|
||||
//increases or decreases the amount of bits used per BlockState
|
||||
//based on the size of the palette.
|
||||
|
||||
long[] blockStates = section.getBlockStates();
|
||||
long[] newBlockStates;
|
||||
|
||||
if (dataVersion < 2527) {
|
||||
newBlockStates = newBits == blockStates.length / 64 ? blockStates : new long[newBits * 64];
|
||||
} else {
|
||||
int newLength = (int) Math.ceil(4096D / (64D / newBits));
|
||||
newBlockStates = newBits == blockStates.length / 64 ? blockStates : new long[newLength];
|
||||
}
|
||||
for (int i = 0; i < 4096; i++) {
|
||||
section.setPaletteIndex(i, section.getPaletteIndex(i), newBlockStates);
|
||||
}
|
||||
section.setBlockStates(newBlockStates);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package com.loohp.limbo.Utils;
|
||||
|
||||
public class CustomArrayUtils {
|
||||
|
||||
public static int[] longArrayToIntArray(long[] numbers) {
|
||||
int[] intNumbers = new int[numbers.length];
|
||||
for(int i = 0; i < numbers.length; i++) {
|
||||
intNumbers[i] = (int) numbers[i];
|
||||
}
|
||||
return intNumbers;
|
||||
}
|
||||
|
||||
public static byte[] longArrayToByteArray(long[] numbers) {
|
||||
byte[] intNumbers = new byte[numbers.length];
|
||||
for(int i = 0; i < numbers.length; i++) {
|
||||
intNumbers[i] = (byte) numbers[i];
|
||||
}
|
||||
return intNumbers;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,180 @@
|
||||
package com.loohp.limbo.Utils;
|
||||
|
||||
import org.json.simple.JSONArray;
|
||||
import org.json.simple.JSONObject;
|
||||
|
||||
import net.querz.nbt.tag.ByteArrayTag;
|
||||
import net.querz.nbt.tag.ByteTag;
|
||||
import net.querz.nbt.tag.CompoundTag;
|
||||
import net.querz.nbt.tag.DoubleTag;
|
||||
import net.querz.nbt.tag.FloatTag;
|
||||
import net.querz.nbt.tag.IntArrayTag;
|
||||
import net.querz.nbt.tag.IntTag;
|
||||
import net.querz.nbt.tag.ListTag;
|
||||
import net.querz.nbt.tag.LongArrayTag;
|
||||
import net.querz.nbt.tag.LongTag;
|
||||
import net.querz.nbt.tag.ShortTag;
|
||||
import net.querz.nbt.tag.StringTag;
|
||||
import net.querz.nbt.tag.Tag;
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
public class CustomNBTUtils {
|
||||
|
||||
public enum TagClass {
|
||||
CompoundTagClass(CompoundTag.class),
|
||||
ByteTagClass(ByteTag.class),
|
||||
ShortTagClass(ShortTag.class),
|
||||
IntTagClass(IntTag.class),
|
||||
LongTagClass(LongTag.class),
|
||||
FloatTagClass(FloatTag.class),
|
||||
DoubleTagClass(DoubleTag.class),
|
||||
ByteArrayTagClass(ByteArrayTag.class),
|
||||
IntArrayTagClass(IntArrayTag.class),
|
||||
LongArrayTagClass(LongArrayTag.class),
|
||||
StringTagClass(StringTag.class),
|
||||
ListTagClass(ListTag.class);
|
||||
|
||||
Class<? extends Tag> clazz;
|
||||
|
||||
TagClass(Class<? extends Tag> clazz) {
|
||||
this.clazz = clazz;
|
||||
}
|
||||
|
||||
public Class<? extends Tag> getTagClass() {
|
||||
return clazz;
|
||||
}
|
||||
}
|
||||
|
||||
public static Class<? extends Tag> getClassFromName(String name) {
|
||||
for (TagClass clazz : TagClass.values()) {
|
||||
if (clazz.getTagClass().getSimpleName().equals(name)) {
|
||||
return clazz.getTagClass();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static CompoundTag getCompoundTagFromJson(JSONObject json) {
|
||||
CompoundTag tag = new CompoundTag();
|
||||
|
||||
for (Object obj : json.keySet()) {
|
||||
String key = (String) obj;
|
||||
JSONObject inside = (JSONObject) json.get(key);
|
||||
String type = (String) inside.get("type");
|
||||
|
||||
switch (type) {
|
||||
case "ByteTag":
|
||||
tag.putByte(key, (byte) (long) inside.get("value"));
|
||||
break;
|
||||
case "ShortTag":
|
||||
tag.putShort(key, (short) (long) inside.get("value"));
|
||||
break;
|
||||
case "IntTag":
|
||||
tag.putInt(key, (int) (long) inside.get("value"));
|
||||
break;
|
||||
case "LongTag":
|
||||
tag.putLong(key, (long) inside.get("value"));
|
||||
break;
|
||||
case "FloatTag":
|
||||
tag.putFloat(key, inside.get("value") instanceof Long ? (float) (long) inside.get("value") : (float) (double) inside.get("value"));
|
||||
break;
|
||||
case "DoubleTag":
|
||||
tag.putDouble(key, inside.get("value") instanceof Long ? (double) (long) inside.get("value") : (double) inside.get("value"));
|
||||
break;
|
||||
case "ByteArrayTag":
|
||||
tag.putByteArray(key, CustomArrayUtils.longArrayToByteArray((long[]) inside.get("value")));
|
||||
break;
|
||||
case "IntArrayTag":
|
||||
tag.putIntArray(key, CustomArrayUtils.longArrayToIntArray((long[]) inside.get("value")));
|
||||
break;
|
||||
case "LongArrayTag":
|
||||
tag.putLongArray(key, (long[]) inside.get("value"));
|
||||
break;
|
||||
case "StringTag":
|
||||
tag.putString(key, (String) inside.get("value"));
|
||||
break;
|
||||
case "CompoundTag":
|
||||
tag.put(key, getCompoundTagFromJson((JSONObject) inside.get("value")));
|
||||
break;
|
||||
case "ListTag":
|
||||
tag.put(key, getListTagFromJson((JSONObject) inside.get("value")));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return tag;
|
||||
}
|
||||
|
||||
public static ListTag<?> getListTagFromJson(JSONObject json) {
|
||||
String type = (String) json.get("type");
|
||||
JSONArray array = (JSONArray) json.get("list");
|
||||
|
||||
ListTag<?> listTag = ListTag.createUnchecked(getClassFromName(type));
|
||||
|
||||
switch (type) {
|
||||
case "ByteTag":
|
||||
for (Object obj : array) {
|
||||
listTag.addByte((byte) (long) obj);
|
||||
}
|
||||
break;
|
||||
case "ShortTag":
|
||||
for (Object obj : array) {
|
||||
listTag.addShort((short) (long) obj);
|
||||
}
|
||||
break;
|
||||
case "IntTag":
|
||||
for (Object obj : array) {
|
||||
listTag.addInt((int) (long) obj);
|
||||
}
|
||||
break;
|
||||
case "LongTag":
|
||||
for (Object obj : array) {
|
||||
listTag.addLong((long) obj);
|
||||
}
|
||||
break;
|
||||
case "FloatTag":
|
||||
for (Object obj : array) {
|
||||
listTag.addFloat(obj instanceof Long ? (float) (long) obj : (float) (double) obj);
|
||||
}
|
||||
break;
|
||||
case "DoubleTag":
|
||||
for (Object obj : array) {
|
||||
listTag.addDouble(obj instanceof Long ? (double) (long) obj : (double) obj);
|
||||
}
|
||||
break;
|
||||
case "ByteArrayTag":
|
||||
for (Object obj : array) {
|
||||
listTag.addByteArray(CustomArrayUtils.longArrayToByteArray((long[]) obj));
|
||||
}
|
||||
break;
|
||||
case "IntArrayTag":
|
||||
for (Object obj : array) {
|
||||
listTag.addIntArray(CustomArrayUtils.longArrayToIntArray((long[]) obj));
|
||||
}
|
||||
break;
|
||||
case "LongArrayTag":
|
||||
for (Object obj : array) {
|
||||
listTag.addLongArray((long[]) obj);
|
||||
}
|
||||
break;
|
||||
case "StringTag":
|
||||
for (Object obj : array) {
|
||||
listTag.addString((String) obj);
|
||||
}
|
||||
break;
|
||||
case "CompoundTag":
|
||||
for (Object obj : array) {
|
||||
listTag.asCompoundTagList().add(getCompoundTagFromJson((JSONObject) obj));
|
||||
}
|
||||
break;
|
||||
case "ListTag":
|
||||
for (Object obj : array) {
|
||||
listTag.asListTagList().add(getListTagFromJson((JSONObject) obj));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return listTag;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
package com.loohp.limbo.Utils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
public class CustomStringUtils {
|
||||
|
||||
public static boolean arrayContains(String compare, String[] args, boolean IgnoreCase) {
|
||||
return IgnoreCase ? Arrays.asList(args).stream().anyMatch(each -> each.equalsIgnoreCase(compare)) : Arrays.asList(args).stream().anyMatch(each -> each.equals(compare));
|
||||
}
|
||||
|
||||
public static boolean arrayContains(String compare, String[] args) {
|
||||
return arrayContains(compare, args, true);
|
||||
}
|
||||
|
||||
public static String[] splitStringToArgs(String str) {
|
||||
List<String> tokens = new ArrayList<String>();
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
boolean insideQuote = false;
|
||||
|
||||
for (char c : str.toCharArray()) {
|
||||
if (c == '"') {
|
||||
insideQuote = !insideQuote;
|
||||
} else if (c == ' ' && !insideQuote) {
|
||||
if (sb.length() > 0) {
|
||||
tokens.add(sb.toString());
|
||||
}
|
||||
sb.delete(0, sb.length());
|
||||
} else {
|
||||
sb.append(c);
|
||||
}
|
||||
}
|
||||
tokens.add(sb.toString());
|
||||
|
||||
return tokens.toArray(new String[tokens.size()]);
|
||||
}
|
||||
|
||||
public static int getIndexOfArg(String str, int ordinal) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
boolean insideQuote = false;
|
||||
|
||||
int pos = 0;
|
||||
int found = 0;
|
||||
for (char c : str.toCharArray()) {
|
||||
if (c == '"') {
|
||||
insideQuote = !insideQuote;
|
||||
} else if (c == ' ' && !insideQuote) {
|
||||
if (sb.length() > 0) {
|
||||
found++;
|
||||
}
|
||||
sb.delete(0, sb.length());
|
||||
} else {
|
||||
sb.append(c);
|
||||
}
|
||||
if (found == ordinal) {
|
||||
return pos;
|
||||
}
|
||||
pos++;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
package com.loohp.limbo.Utils;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.UUID;
|
||||
|
||||
import com.loohp.limbo.World.BlockPosition;
|
||||
|
||||
import net.querz.nbt.io.NBTOutputStream;
|
||||
import net.querz.nbt.tag.CompoundTag;
|
||||
import net.querz.nbt.tag.Tag;
|
||||
|
||||
public class DataTypeIO {
|
||||
|
||||
public static void writeBlockPosition(DataOutputStream out, BlockPosition position) throws IOException {
|
||||
out.writeLong(((position.getX() & 0x3FFFFFF) << 38) | ((position.getZ() & 0x3FFFFFF) << 12) | (position.getY() & 0xFFF));
|
||||
}
|
||||
|
||||
public static void writeUUID(DataOutputStream out, UUID uuid) throws IOException {
|
||||
out.writeLong(uuid.getMostSignificantBits());
|
||||
out.writeLong(uuid.getLeastSignificantBits());
|
||||
}
|
||||
|
||||
public static void writeCompoundTag(DataOutputStream out, CompoundTag tag) throws IOException {
|
||||
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
|
||||
|
||||
DataOutputStream output = new DataOutputStream(buffer);
|
||||
new NBTOutputStream(output).writeTag(tag, Tag.DEFAULT_MAX_DEPTH);
|
||||
|
||||
byte[] b = buffer.toByteArray();
|
||||
out.write(b);
|
||||
}
|
||||
|
||||
public static String readString(DataInputStream in, Charset charset) throws IOException {
|
||||
int length = readVarInt(in);
|
||||
|
||||
if (length == -1) {
|
||||
throw new IOException("Premature end of stream.");
|
||||
}
|
||||
|
||||
byte[] b = new byte[length];
|
||||
in.readFully(b);
|
||||
return new String(b, charset);
|
||||
}
|
||||
|
||||
public static int getStringLength(String string, Charset charset) throws IOException {
|
||||
byte[] bytes = string.getBytes(charset);
|
||||
return bytes.length;
|
||||
}
|
||||
|
||||
public static void writeString(DataOutputStream out, String string, Charset charset) throws IOException {
|
||||
byte[] bytes = string.getBytes(charset);
|
||||
writeVarInt(out, bytes.length);
|
||||
out.write(bytes);
|
||||
}
|
||||
|
||||
public static int readVarInt(DataInputStream in) throws IOException {
|
||||
int numRead = 0;
|
||||
int result = 0;
|
||||
byte read;
|
||||
do {
|
||||
read = in.readByte();
|
||||
int value = (read & 0b01111111);
|
||||
result |= (value << (7 * numRead));
|
||||
|
||||
numRead++;
|
||||
if (numRead > 5) {
|
||||
throw new RuntimeException("VarInt is too big");
|
||||
}
|
||||
} while ((read & 0b10000000) != 0);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static void writeVarInt(DataOutputStream out, int value) throws IOException {
|
||||
do {
|
||||
byte temp = (byte)(value & 0b01111111);
|
||||
// Note: >>> means that the sign bit is shifted with the rest of the number rather than being left alone
|
||||
value >>>= 7;
|
||||
if (value != 0) {
|
||||
temp |= 0b10000000;
|
||||
}
|
||||
out.writeByte(temp);
|
||||
} while (value != 0);
|
||||
}
|
||||
|
||||
public static int getVarIntLength(int value) throws IOException {
|
||||
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
|
||||
DataOutputStream out = new DataOutputStream(buffer);
|
||||
do {
|
||||
byte temp = (byte)(value & 0b01111111);
|
||||
// Note: >>> means that the sign bit is shifted with the rest of the number rather than being left alone
|
||||
value >>>= 7;
|
||||
if (value != 0) {
|
||||
temp |= 0b10000000;
|
||||
}
|
||||
out.writeByte(temp);
|
||||
} while (value != 0);
|
||||
return buffer.toByteArray().length;
|
||||
}
|
||||
|
||||
public static long readVarLong(DataInputStream in) throws IOException {
|
||||
int numRead = 0;
|
||||
long result = 0;
|
||||
byte read;
|
||||
do {
|
||||
read = in.readByte();
|
||||
long value = (read & 0b01111111);
|
||||
result |= (value << (7 * numRead));
|
||||
|
||||
numRead++;
|
||||
if (numRead > 10) {
|
||||
throw new RuntimeException("VarLong is too big");
|
||||
}
|
||||
} while ((read & 0b10000000) != 0);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static void writeVarLong(DataOutputStream out, long value) throws IOException {
|
||||
do {
|
||||
byte temp = (byte)(value & 0b01111111);
|
||||
// Note: >>> means that the sign bit is shifted with the rest of the number rather than being left alone
|
||||
value >>>= 7;
|
||||
if (value != 0) {
|
||||
temp |= 0b10000000;
|
||||
}
|
||||
out.writeByte(temp);
|
||||
} while (value != 0);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package com.loohp.limbo.Utils;
|
||||
|
||||
public enum GameMode {
|
||||
|
||||
SURVIVAL(0, "survival"),
|
||||
CREATIVE(1, "creative"),
|
||||
ADVENTURE(2, "adventure"),
|
||||
SPECTATOR(3, "spectator");
|
||||
|
||||
int id;
|
||||
String name;
|
||||
|
||||
GameMode(int id, String name) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public static GameMode fromId(int id) {
|
||||
for (GameMode mode : GameMode.values()) {
|
||||
if (mode.getId() == id) {
|
||||
return mode;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static GameMode fromName(String name) {
|
||||
for (GameMode mode : GameMode.values()) {
|
||||
if (mode.getName().equalsIgnoreCase(name)) {
|
||||
return mode;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.loohp.limbo.Utils;
|
||||
|
||||
import java.awt.image.RenderedImage;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.Base64;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
|
||||
public class ImageUtils {
|
||||
|
||||
public static String imgToBase64String(final RenderedImage img, String formatName) throws IOException {
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
ImageIO.write(img, formatName, out);
|
||||
return Base64.getEncoder().encodeToString(out.toByteArray());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
package com.loohp.limbo.Utils;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.URL;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.net.ssl.HttpsURLConnection;
|
||||
|
||||
public class MojangAPIUtils {
|
||||
|
||||
public static class SkinResponse {
|
||||
|
||||
String skin;
|
||||
String signature;
|
||||
|
||||
public SkinResponse(String skin, String signature) {
|
||||
this.skin = skin;
|
||||
this.signature = signature;
|
||||
}
|
||||
|
||||
public String getSkin() {
|
||||
return skin;
|
||||
}
|
||||
|
||||
public String getSignature() {
|
||||
return signature;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static UUID getOnlineUUIDOfPlayerFromMojang(String username) {
|
||||
try {
|
||||
URL url = new URL("https://api.mojang.com/users/profiles/minecraft/" + username);
|
||||
HttpsURLConnection connection = (HttpsURLConnection) url.openConnection();
|
||||
connection.setUseCaches(false);
|
||||
connection.setDefaultUseCaches(false);
|
||||
connection.addRequestProperty("User-Agent", "Mozilla/5.0");
|
||||
connection.addRequestProperty("Cache-Control", "no-cache, no-store, must-revalidate");
|
||||
connection.addRequestProperty("Pragma", "no-cache");
|
||||
if (connection.getResponseCode() == HttpsURLConnection.HTTP_OK) {
|
||||
String reply = new BufferedReader(new InputStreamReader(connection.getInputStream())).readLine();
|
||||
if (!reply.contains("\"error\":\"BadRequestException\"")) {
|
||||
String uuid = reply.split("\"id\":\"")[1].split("\"")[0];
|
||||
return UUID.fromString(uuid.replaceFirst("([0-9a-fA-F]{8})([0-9a-fA-F]{4})([0-9a-fA-F]{4})([0-9a-fA-F]{4})([0-9a-fA-F]+)", "$1-$2-$3-$4-$5"));
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
System.err.println("Connection could not be opened (Response code " + connection.getResponseCode() + ", " + connection.getResponseMessage() + ")");
|
||||
return null;
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static SkinResponse getSkinFromMojangServer(String username) {
|
||||
UUID uuid = getOnlineUUIDOfPlayerFromMojang(username);
|
||||
if (uuid == null) {
|
||||
return null;
|
||||
}
|
||||
return getSkinFromMojangServer(uuid);
|
||||
}
|
||||
|
||||
public static SkinResponse getSkinFromMojangServer(UUID uuid) {
|
||||
try {
|
||||
URL url = new URL("https://sessionserver.mojang.com/session/minecraft/profile/" + uuid.toString() + "?unsigned=false");
|
||||
HttpsURLConnection connection = (HttpsURLConnection) url.openConnection();
|
||||
connection.setUseCaches(false);
|
||||
connection.setDefaultUseCaches(false);
|
||||
connection.addRequestProperty("User-Agent", "Mozilla/5.0");
|
||||
connection.addRequestProperty("Cache-Control", "no-cache, no-store, must-revalidate");
|
||||
connection.addRequestProperty("Pragma", "no-cache");
|
||||
if (connection.getResponseCode() == HttpsURLConnection.HTTP_OK) {
|
||||
String reply = String.join("", new BufferedReader(new InputStreamReader(connection.getInputStream())).lines().collect(Collectors.toList())).replace(" ", "");
|
||||
String skin = reply.split("\"value\":\"")[1].split("\"")[0];
|
||||
String signature = reply.split("\"signature\":\"")[1].split("\"")[0];
|
||||
return new SkinResponse(skin, signature);
|
||||
} else {
|
||||
System.err.println("Connection could not be opened (Response code " + connection.getResponseCode() + ", " + connection.getResponseMessage() + ")");
|
||||
return null;
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
package com.loohp.limbo.Utils;
|
||||
|
||||
public class NamespacedKey {
|
||||
|
||||
private String namespace;
|
||||
private String key;
|
||||
|
||||
public NamespacedKey(String namespacedKey) {
|
||||
int index = namespacedKey.indexOf(":");
|
||||
if (index >= 0) {
|
||||
this.namespace = namespacedKey.substring(0, index);
|
||||
this.key = namespacedKey.substring(index + 1);
|
||||
} else {
|
||||
this.namespace = "minecraft";
|
||||
this.key = namespacedKey;
|
||||
}
|
||||
}
|
||||
|
||||
public NamespacedKey(String namespace, String key) {
|
||||
this.namespace = namespace;
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
public String getNamespace() {
|
||||
return namespace;
|
||||
}
|
||||
|
||||
public String getKey() {
|
||||
return key;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return namespace + ":" + key;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + ((key == null) ? 0 : key.hashCode());
|
||||
result = prime * result + ((namespace == null) ? 0 : namespace.hashCode());
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj)
|
||||
return true;
|
||||
if (obj == null)
|
||||
return false;
|
||||
if (getClass() != obj.getClass())
|
||||
return false;
|
||||
NamespacedKey other = (NamespacedKey) obj;
|
||||
if (key == null) {
|
||||
if (other.key != null)
|
||||
return false;
|
||||
} else if (!key.equals(other.key))
|
||||
return false;
|
||||
if (namespace == null) {
|
||||
if (other.namespace != null)
|
||||
return false;
|
||||
} else if (!namespace.equals(other.namespace))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package com.loohp.limbo.Utils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.DatagramSocket;
|
||||
import java.net.ServerSocket;
|
||||
|
||||
public class NetworkUtils {
|
||||
|
||||
public static boolean available(int port) {
|
||||
ServerSocket ss = null;
|
||||
DatagramSocket ds = null;
|
||||
try {
|
||||
ss = new ServerSocket(port);
|
||||
ss.setReuseAddress(true);
|
||||
ds = new DatagramSocket(port);
|
||||
ds.setReuseAddress(true);
|
||||
return true;
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
if (ds != null) {
|
||||
ds.close();
|
||||
}
|
||||
|
||||
if (ss != null) {
|
||||
try {
|
||||
ss.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
package com.loohp.limbo.Utils;
|
||||
|
||||
/**
|
||||
* Utils for casting number types to other number types
|
||||
*/
|
||||
public final class NumberConversions {
|
||||
private NumberConversions() {}
|
||||
|
||||
public static int floor(double num) {
|
||||
final int floor = (int) num;
|
||||
return floor == num ? floor : floor - (int) (Double.doubleToRawLongBits(num) >>> 63);
|
||||
}
|
||||
|
||||
public static int ceil(final double num) {
|
||||
final int floor = (int) num;
|
||||
return floor == num ? floor : floor + (int) (~Double.doubleToRawLongBits(num) >>> 63);
|
||||
}
|
||||
|
||||
public static int round(double num) {
|
||||
return floor(num + 0.5d);
|
||||
}
|
||||
|
||||
public static double square(double num) {
|
||||
return num * num;
|
||||
}
|
||||
|
||||
public static int toInt(Object object) {
|
||||
if (object instanceof Number) {
|
||||
return ((Number) object).intValue();
|
||||
}
|
||||
|
||||
try {
|
||||
return Integer.parseInt(object.toString());
|
||||
} catch (NumberFormatException e) {
|
||||
} catch (NullPointerException e) {
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static float toFloat(Object object) {
|
||||
if (object instanceof Number) {
|
||||
return ((Number) object).floatValue();
|
||||
}
|
||||
|
||||
try {
|
||||
return Float.parseFloat(object.toString());
|
||||
} catch (NumberFormatException e) {
|
||||
} catch (NullPointerException e) {
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static double toDouble(Object object) {
|
||||
if (object instanceof Number) {
|
||||
return ((Number) object).doubleValue();
|
||||
}
|
||||
|
||||
try {
|
||||
return Double.parseDouble(object.toString());
|
||||
} catch (NumberFormatException e) {
|
||||
} catch (NullPointerException e) {
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static long toLong(Object object) {
|
||||
if (object instanceof Number) {
|
||||
return ((Number) object).longValue();
|
||||
}
|
||||
|
||||
try {
|
||||
return Long.parseLong(object.toString());
|
||||
} catch (NumberFormatException e) {
|
||||
} catch (NullPointerException e) {
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static short toShort(Object object) {
|
||||
if (object instanceof Number) {
|
||||
return ((Number) object).shortValue();
|
||||
}
|
||||
|
||||
try {
|
||||
return Short.parseShort(object.toString());
|
||||
} catch (NumberFormatException e) {
|
||||
} catch (NullPointerException e) {
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static byte toByte(Object object) {
|
||||
if (object instanceof Number) {
|
||||
return ((Number) object).byteValue();
|
||||
}
|
||||
|
||||
try {
|
||||
return Byte.parseByte(object.toString());
|
||||
} catch (NumberFormatException e) {
|
||||
} catch (NullPointerException e) {
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static boolean isFinite(double d) {
|
||||
return Math.abs(d) <= Double.MAX_VALUE;
|
||||
}
|
||||
|
||||
public static boolean isFinite(float f) {
|
||||
return Math.abs(f) <= Float.MAX_VALUE;
|
||||
}
|
||||
|
||||
public static void checkFinite(double d, String message) {
|
||||
if (!isFinite(d)) {
|
||||
throw new IllegalArgumentException(message);
|
||||
}
|
||||
}
|
||||
|
||||
public static void checkFinite(float d, String message) {
|
||||
if (!isFinite(d)) {
|
||||
throw new IllegalArgumentException(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package com.loohp.limbo.Utils;
|
||||
|
||||
import net.querz.nbt.tag.CompoundTag;
|
||||
|
||||
public class SchematicConvertionUtils {
|
||||
|
||||
public static CompoundTag toTileEntityTag(CompoundTag tag) {
|
||||
int[] pos = tag.getIntArray("Pos");
|
||||
tag.remove("Pos");
|
||||
tag.remove("Id");
|
||||
tag.putInt("x", pos[0]);
|
||||
tag.putInt("y", pos[1]);
|
||||
tag.putInt("z", pos[2]);
|
||||
return tag;
|
||||
}
|
||||
|
||||
public static CompoundTag toBlockTag(String input) {
|
||||
int index = input.indexOf("[");
|
||||
CompoundTag tag = new CompoundTag();
|
||||
if (index < 0) {
|
||||
tag.putString("Name", new NamespacedKey(input).toString());
|
||||
return tag;
|
||||
}
|
||||
|
||||
tag.putString("Name", new NamespacedKey(input.substring(0, index)).toString());
|
||||
|
||||
String[] states = input.substring(index + 1, input.lastIndexOf("]")).replace(" ", "").split(",");
|
||||
|
||||
CompoundTag properties = new CompoundTag();
|
||||
for (String state : states) {
|
||||
String key = state.substring(0, state.indexOf("="));
|
||||
String value = state.substring(state.indexOf("=") + 1);
|
||||
properties.putString(key, value);
|
||||
}
|
||||
|
||||
tag.put("Properties", properties);
|
||||
|
||||
return tag;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,184 @@
|
||||
package com.loohp.limbo.Utils;
|
||||
|
||||
import java.beans.FeatureDescriptor;
|
||||
import java.beans.IntrospectionException;
|
||||
import java.beans.Introspector;
|
||||
import java.beans.PropertyDescriptor;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.yaml.snakeyaml.error.YAMLException;
|
||||
import org.yaml.snakeyaml.introspector.BeanAccess;
|
||||
import org.yaml.snakeyaml.introspector.FieldProperty;
|
||||
import org.yaml.snakeyaml.introspector.MethodProperty;
|
||||
import org.yaml.snakeyaml.introspector.MissingProperty;
|
||||
import org.yaml.snakeyaml.introspector.Property;
|
||||
import org.yaml.snakeyaml.introspector.PropertyUtils;
|
||||
import org.yaml.snakeyaml.util.PlatformFeatureDetector;
|
||||
|
||||
public class YamlOrder extends PropertyUtils {
|
||||
|
||||
private final Map<Class<?>, Map<String, Property>> propertiesCache = new HashMap<Class<?>, Map<String, Property>>();
|
||||
private final Map<Class<?>, Set<Property>> readableProperties = new HashMap<Class<?>, Set<Property>>();
|
||||
private BeanAccess beanAccess = BeanAccess.DEFAULT;
|
||||
private boolean allowReadOnlyProperties = false;
|
||||
private boolean skipMissingProperties = false;
|
||||
|
||||
private PlatformFeatureDetector platformFeatureDetector;
|
||||
|
||||
public YamlOrder() {
|
||||
this(new PlatformFeatureDetector());
|
||||
}
|
||||
|
||||
YamlOrder(PlatformFeatureDetector platformFeatureDetector) {
|
||||
this.platformFeatureDetector = platformFeatureDetector;
|
||||
|
||||
if (platformFeatureDetector.isRunningOnAndroid()) {
|
||||
beanAccess = BeanAccess.FIELD;
|
||||
}
|
||||
}
|
||||
|
||||
protected Map<String, Property> getPropertiesMap(Class<?> type, BeanAccess bAccess) {
|
||||
if (propertiesCache.containsKey(type)) {
|
||||
return propertiesCache.get(type);
|
||||
}
|
||||
|
||||
Map<String, Property> properties = new LinkedHashMap<String, Property>();
|
||||
boolean inaccessableFieldsExist = false;
|
||||
switch (bAccess) {
|
||||
case FIELD:
|
||||
for (Class<?> c = type; c != null; c = c.getSuperclass()) {
|
||||
for (Field field : c.getDeclaredFields()) {
|
||||
int modifiers = field.getModifiers();
|
||||
if (!Modifier.isStatic(modifiers) && !Modifier.isTransient(modifiers)
|
||||
&& !properties.containsKey(field.getName())) {
|
||||
properties.put(field.getName(), new FieldProperty(field));
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
try {
|
||||
for (PropertyDescriptor property : Introspector.getBeanInfo(type)
|
||||
.getPropertyDescriptors()) {
|
||||
Method readMethod = property.getReadMethod();
|
||||
if ((readMethod == null || !readMethod.getName().equals("getClass"))
|
||||
&& !isTransient(property)) {
|
||||
properties.put(property.getName(), new MethodProperty(property));
|
||||
}
|
||||
}
|
||||
} catch (IntrospectionException e) {
|
||||
throw new YAMLException(e);
|
||||
}
|
||||
|
||||
for (Class<?> c = type; c != null; c = c.getSuperclass()) {
|
||||
for (Field field : c.getDeclaredFields()) {
|
||||
int modifiers = field.getModifiers();
|
||||
if (!Modifier.isStatic(modifiers) && !Modifier.isTransient(modifiers)) {
|
||||
if (Modifier.isPublic(modifiers)) {
|
||||
properties.put(field.getName(), new FieldProperty(field));
|
||||
} else {
|
||||
inaccessableFieldsExist = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (properties.isEmpty() && inaccessableFieldsExist) {
|
||||
throw new YAMLException("No JavaBean properties found in " + type.getName());
|
||||
}
|
||||
System.out.println(properties);
|
||||
propertiesCache.put(type, properties);
|
||||
return properties;
|
||||
}
|
||||
|
||||
private static final String TRANSIENT = "transient";
|
||||
|
||||
private boolean isTransient(FeatureDescriptor fd) {
|
||||
return Boolean.TRUE.equals(fd.getValue(TRANSIENT));
|
||||
}
|
||||
|
||||
public Set<Property> getProperties(Class<? extends Object> type) {
|
||||
return getProperties(type, beanAccess);
|
||||
}
|
||||
|
||||
public Set<Property> getProperties(Class<? extends Object> type, BeanAccess bAccess) {
|
||||
if (readableProperties.containsKey(type)) {
|
||||
return readableProperties.get(type);
|
||||
}
|
||||
Set<Property> properties = createPropertySet(type, bAccess);
|
||||
readableProperties.put(type, properties);
|
||||
return properties;
|
||||
}
|
||||
|
||||
protected Set<Property> createPropertySet(Class<? extends Object> type, BeanAccess bAccess) {
|
||||
Set<Property> properties = new LinkedHashSet<>();
|
||||
Collection<Property> props = getPropertiesMap(type, bAccess).values();
|
||||
for (Property property : props) {
|
||||
if (property.isReadable() && (allowReadOnlyProperties || property.isWritable())) {
|
||||
properties.add(property);
|
||||
}
|
||||
}
|
||||
return properties;
|
||||
}
|
||||
|
||||
public Property getProperty(Class<? extends Object> type, String name) {
|
||||
return getProperty(type, name, beanAccess);
|
||||
}
|
||||
|
||||
public Property getProperty(Class<? extends Object> type, String name, BeanAccess bAccess) {
|
||||
Map<String, Property> properties = getPropertiesMap(type, bAccess);
|
||||
Property property = properties.get(name);
|
||||
if (property == null && skipMissingProperties) {
|
||||
property = new MissingProperty(name);
|
||||
}
|
||||
if (property == null) {
|
||||
throw new YAMLException(
|
||||
"Unable to find property '" + name + "' on class: " + type.getName());
|
||||
}
|
||||
return property;
|
||||
}
|
||||
|
||||
public void setBeanAccess(BeanAccess beanAccess) {
|
||||
if (platformFeatureDetector.isRunningOnAndroid() && beanAccess != BeanAccess.FIELD) {
|
||||
throw new IllegalArgumentException(
|
||||
"JVM is Android - only BeanAccess.FIELD is available");
|
||||
}
|
||||
|
||||
if (this.beanAccess != beanAccess) {
|
||||
this.beanAccess = beanAccess;
|
||||
propertiesCache.clear();
|
||||
readableProperties.clear();
|
||||
}
|
||||
}
|
||||
|
||||
public void setAllowReadOnlyProperties(boolean allowReadOnlyProperties) {
|
||||
if (this.allowReadOnlyProperties != allowReadOnlyProperties) {
|
||||
this.allowReadOnlyProperties = allowReadOnlyProperties;
|
||||
readableProperties.clear();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isAllowReadOnlyProperties() {
|
||||
return allowReadOnlyProperties;
|
||||
}
|
||||
|
||||
public void setSkipMissingProperties(boolean skipMissingProperties) {
|
||||
if (this.skipMissingProperties != skipMissingProperties) {
|
||||
this.skipMissingProperties = skipMissingProperties;
|
||||
readableProperties.clear();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isSkipMissingProperties() {
|
||||
return skipMissingProperties;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package com.loohp.limbo.World;
|
||||
|
||||
import com.loohp.limbo.Location.Location;
|
||||
|
||||
public class BlockPosition {
|
||||
private int x;
|
||||
private int y;
|
||||
private int z;
|
||||
|
||||
public BlockPosition(int x, int y, int z) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.z = z;
|
||||
}
|
||||
|
||||
public int getX() {
|
||||
return this.x;
|
||||
}
|
||||
|
||||
public int getY() {
|
||||
return this.y;
|
||||
}
|
||||
|
||||
public int getZ() {
|
||||
return this.z;
|
||||
}
|
||||
|
||||
public static BlockPosition from(Location location) {
|
||||
return new BlockPosition((int) Math.floor(location.getX()), (int) Math.floor(location.getY()), (int) Math.floor(location.getZ()));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
package com.loohp.limbo.World;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import com.loohp.limbo.Utils.NamespacedKey;
|
||||
|
||||
import net.querz.nbt.tag.CompoundTag;
|
||||
import net.querz.nbt.tag.StringTag;
|
||||
import net.querz.nbt.tag.Tag;
|
||||
|
||||
public class BlockState {
|
||||
|
||||
private CompoundTag tag;
|
||||
|
||||
public BlockState(CompoundTag tag) {
|
||||
this.tag = tag;
|
||||
}
|
||||
|
||||
public CompoundTag toCompoundTag() {
|
||||
return tag;
|
||||
}
|
||||
|
||||
public NamespacedKey getType() {
|
||||
return new NamespacedKey(tag.getString("Name"));
|
||||
}
|
||||
|
||||
public void setType(NamespacedKey namespacedKey) {
|
||||
tag.putString("Name", namespacedKey.toString());
|
||||
}
|
||||
|
||||
public Map<String, String> getProperties() {
|
||||
Map<String, String> mapping = new HashMap<>();
|
||||
for (Entry<String, Tag<?>> entry : tag.getCompoundTag("Properties")) {
|
||||
String key = entry.getKey();
|
||||
String value = ((StringTag) entry.getValue()).getValue();
|
||||
mapping.put(key, value);
|
||||
}
|
||||
return mapping;
|
||||
}
|
||||
|
||||
public String getProperty(String key) {
|
||||
Tag<?> value = tag.getCompoundTag("Properties").get(key);
|
||||
return value == null ? null : ((StringTag) value).getValue();
|
||||
}
|
||||
|
||||
public void setProperties(Map<String, String> mapping) {
|
||||
CompoundTag properties = new CompoundTag();
|
||||
for (Entry<String, String> entry : mapping.entrySet()) {
|
||||
String key = entry.getKey();
|
||||
String value = entry.getValue();
|
||||
properties.putString(key, value);
|
||||
}
|
||||
tag.put("Properties", properties);
|
||||
}
|
||||
|
||||
public <T> void setProperty(String key, T value) {
|
||||
CompoundTag properties = tag.getCompoundTag("Properties");
|
||||
properties.putString(key, ((T) value).toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + ((tag == null) ? 0 : tag.hashCode());
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
if (getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
BlockState other = (BlockState) obj;
|
||||
if (tag == null) {
|
||||
if (other.tag != null) {
|
||||
return false;
|
||||
}
|
||||
} else if (!tag.equals(other.tag)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
package com.loohp.limbo.World;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.file.Files;
|
||||
|
||||
import org.json.simple.JSONObject;
|
||||
import org.json.simple.parser.JSONParser;
|
||||
import org.json.simple.parser.ParseException;
|
||||
|
||||
import com.loohp.limbo.Limbo;
|
||||
import com.loohp.limbo.Utils.CustomNBTUtils;
|
||||
|
||||
import net.querz.nbt.tag.CompoundTag;
|
||||
|
||||
public class DimensionRegistry {
|
||||
|
||||
private CompoundTag defaultTag;
|
||||
private CompoundTag codec;
|
||||
private File reg;
|
||||
|
||||
public DimensionRegistry() {
|
||||
this.defaultTag = new CompoundTag();
|
||||
|
||||
String name = "dimension_registry.json";
|
||||
File file = new File(Limbo.getInstance().getInternalDataFolder(), name);
|
||||
if (!file.exists()) {
|
||||
try (InputStream in = Limbo.class.getClassLoader().getResourceAsStream(name)) {
|
||||
Files.copy(in, file.toPath());
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
this.reg = file;
|
||||
|
||||
try {
|
||||
JSONObject json = (JSONObject) new JSONParser().parse(new FileReader(reg));
|
||||
CompoundTag tag = CustomNBTUtils.getCompoundTagFromJson((JSONObject) json.get("value"));
|
||||
defaultTag = tag;
|
||||
codec = defaultTag.clone();
|
||||
} catch (IOException | ParseException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public File getFile() {
|
||||
return reg;
|
||||
}
|
||||
|
||||
public void resetCodec() {
|
||||
codec = defaultTag.clone();
|
||||
}
|
||||
|
||||
public CompoundTag getCodec() {
|
||||
return codec;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
package com.loohp.limbo.World;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import com.loohp.limbo.Utils.NamespacedKey;
|
||||
|
||||
public class Environment {
|
||||
|
||||
public static final Environment NORMAL = new Environment(new NamespacedKey("minecraft:overworld"), true);
|
||||
public static final Environment NETHER = new Environment(new NamespacedKey("minecraft:the_nether"), false);
|
||||
public static final Environment END = new Environment(new NamespacedKey("minecraft:the_end"), false);
|
||||
|
||||
public static final Set<Environment> REGISTERED_ENVIRONMENTS = new HashSet<>();
|
||||
|
||||
public static Environment fromNamespacedKey(NamespacedKey key) {
|
||||
if (key.equals(NORMAL.getNamespacedKey())) {
|
||||
return NORMAL;
|
||||
} else if (key.equals(NETHER.getNamespacedKey())) {
|
||||
return NETHER;
|
||||
} else if (key.equals(END.getNamespacedKey())) {
|
||||
return END;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public static Environment createCustom(NamespacedKey key) {
|
||||
return createCustom(key, true);
|
||||
}
|
||||
|
||||
public static Environment createCustom(NamespacedKey key, boolean hasSkyLight) {
|
||||
if (REGISTERED_ENVIRONMENTS.stream().anyMatch(each -> each.getNamespacedKey().equals(key))) {
|
||||
throw new IllegalArgumentException("An Environment is already created with this NamespacedKey");
|
||||
}
|
||||
return new Environment(key, hasSkyLight);
|
||||
}
|
||||
|
||||
public static Environment getCustom(NamespacedKey key) {
|
||||
return REGISTERED_ENVIRONMENTS.stream().filter(each -> each.getNamespacedKey().equals(key)).findFirst().orElse(null);
|
||||
}
|
||||
|
||||
//=========================
|
||||
|
||||
private NamespacedKey key;
|
||||
private boolean hasSkyLight;
|
||||
|
||||
private Environment(NamespacedKey key, boolean hasSkyLight) {
|
||||
this.key = key;
|
||||
this.hasSkyLight = hasSkyLight;
|
||||
}
|
||||
|
||||
public NamespacedKey getNamespacedKey() {
|
||||
return key;
|
||||
}
|
||||
|
||||
public boolean hasSkyLight() {
|
||||
return hasSkyLight;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + (hasSkyLight ? 1231 : 1237);
|
||||
result = prime * result + ((key == null) ? 0 : key.hashCode());
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
if (getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
Environment other = (Environment) obj;
|
||||
if (hasSkyLight != other.hasSkyLight) {
|
||||
return false;
|
||||
}
|
||||
if (key == null) {
|
||||
if (other.key != null) {
|
||||
return false;
|
||||
}
|
||||
} else if (!key.equals(other.key)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
package com.loohp.limbo.World;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.file.Files;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.json.simple.JSONArray;
|
||||
import org.json.simple.JSONObject;
|
||||
import org.json.simple.parser.JSONParser;
|
||||
import org.json.simple.parser.ParseException;
|
||||
|
||||
import com.loohp.limbo.Limbo;
|
||||
|
||||
import net.querz.nbt.tag.CompoundTag;
|
||||
|
||||
public class GeneratedBlockDataMappings {
|
||||
|
||||
private static JSONObject globalPalette = new JSONObject();
|
||||
|
||||
static {
|
||||
String block = "blocks.json";
|
||||
File file = new File(Limbo.getInstance().getInternalDataFolder(), block);
|
||||
if (!file.exists()) {
|
||||
try (InputStream in = Limbo.class.getClassLoader().getResourceAsStream(block)) {
|
||||
Files.copy(in, file.toPath());
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
globalPalette = (JSONObject) new JSONParser().parse(new FileReader(file));
|
||||
} catch (IOException | ParseException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static int getGlobalPaletteIDFromState(CompoundTag tag) {
|
||||
String blockname = tag.getString("Name");
|
||||
|
||||
JSONObject data = (JSONObject) globalPalette.get(blockname);
|
||||
Object obj = data.get("properties");
|
||||
if (obj == null) {
|
||||
return (int) (long) ((JSONObject) ((JSONArray) data.get("states")).get(0)).get("id");
|
||||
}
|
||||
|
||||
//JSONObject properties = (JSONObject) obj;
|
||||
|
||||
if (tag.containsKey("Properties")) {
|
||||
CompoundTag blockProp = tag.get("Properties", CompoundTag.class);
|
||||
Map<String, String> blockstate = new HashMap<>();
|
||||
for (String key : blockProp.keySet()) {
|
||||
blockstate.put(key, blockProp.getString(key));
|
||||
}
|
||||
|
||||
for (Object entry : (JSONArray) data.get("states")) {
|
||||
JSONObject jsonobj = (JSONObject) entry;
|
||||
if (((JSONObject) jsonobj.get("properties")).keySet().stream().allMatch(key -> blockstate.get(key).equals((String) (((JSONObject) jsonobj.get("properties")).get(key))))) {
|
||||
return (int) (long) jsonobj.get("id");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (Object entry : (JSONArray) data.get("states")) {
|
||||
if (((JSONObject) entry).containsKey("default") && ((boolean) ((JSONObject) entry).get("default"))) {
|
||||
return (int) (long) ((JSONObject) entry).get("id");
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package com.loohp.limbo.World;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public abstract class LightEngine {
|
||||
|
||||
private static Map<String, Byte> blockLightLevelMapping = new HashMap<>();
|
||||
|
||||
static {
|
||||
blockLightLevelMapping.put("minecraft:beacon", (byte) 15);
|
||||
blockLightLevelMapping.put("minecraft:torch", (byte) 15);
|
||||
blockLightLevelMapping.put("minecraft:beacon", (byte) 15);
|
||||
blockLightLevelMapping.put("minecraft:beacon", (byte) 15);
|
||||
blockLightLevelMapping.put("minecraft:beacon", (byte) 15);
|
||||
blockLightLevelMapping.put("minecraft:beacon", (byte) 15);
|
||||
blockLightLevelMapping.put("minecraft:beacon", (byte) 15);
|
||||
blockLightLevelMapping.put("minecraft:beacon", (byte) 15);
|
||||
blockLightLevelMapping.put("minecraft:beacon", (byte) 15);
|
||||
blockLightLevelMapping.put("minecraft:beacon", (byte) 15);
|
||||
blockLightLevelMapping.put("minecraft:beacon", (byte) 15);
|
||||
blockLightLevelMapping.put("minecraft:beacon", (byte) 15);
|
||||
blockLightLevelMapping.put("minecraft:beacon", (byte) 15);
|
||||
blockLightLevelMapping.put("minecraft:beacon", (byte) 15);
|
||||
blockLightLevelMapping.put("minecraft:beacon", (byte) 15);
|
||||
blockLightLevelMapping.put("minecraft:beacon", (byte) 15);
|
||||
blockLightLevelMapping.put("minecraft:beacon", (byte) 15);
|
||||
blockLightLevelMapping.put("minecraft:beacon", (byte) 15);
|
||||
blockLightLevelMapping.put("minecraft:beacon", (byte) 15);
|
||||
blockLightLevelMapping.put("minecraft:beacon", (byte) 15);
|
||||
blockLightLevelMapping.put("minecraft:beacon", (byte) 15);
|
||||
blockLightLevelMapping.put("minecraft:beacon", (byte) 15);
|
||||
blockLightLevelMapping.put("minecraft:beacon", (byte) 15);
|
||||
blockLightLevelMapping.put("minecraft:beacon", (byte) 15);
|
||||
blockLightLevelMapping.put("minecraft:beacon", (byte) 15);
|
||||
|
||||
}
|
||||
|
||||
public static int getBlockLight(BlockState block) {
|
||||
return blockLightLevelMapping.getOrDefault(block.getType().toString(), (byte) 0);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
package com.loohp.limbo.World;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class LightEngineBlock extends LightEngine {
|
||||
|
||||
private World world;
|
||||
private byte[][][] blockLightArray;
|
||||
|
||||
public LightEngineBlock(World world) {
|
||||
blockLightArray = new byte[world.getChunkWidth() * 16][16 * 18][world.getChunkLength() * 16];
|
||||
this.world = world;
|
||||
updateWorld();
|
||||
}
|
||||
|
||||
public void updateWorld() {
|
||||
blockLightArray = new byte[world.getChunkWidth() * 16][16 * 18][world.getChunkLength() * 16];
|
||||
for (int x = 0; x < world.getWidth(); x++) {
|
||||
for (int y = 0; y < 256; y++) {
|
||||
for (int z = 0; z < world.getLength(); z++) {
|
||||
updateBlock(x, y, z);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void updateBlock(int x, int y, int z) {
|
||||
BlockState block = world.getBlock(x, y, z);
|
||||
int lightLevel = getBlockLight(block);
|
||||
if (lightLevel > 0) {
|
||||
propergate(lightLevel, x, y, z);
|
||||
}
|
||||
}
|
||||
|
||||
private void propergate(int level, int x, int y, int z) {
|
||||
if (blockLightArray[x][y + 16][z] < level) {
|
||||
blockLightArray[x][y + 16][z] = (byte) level;
|
||||
if (level > 1) {
|
||||
propergate(level - 1, x + 1, y, z);
|
||||
propergate(level - 1, x - 1, y, z);
|
||||
propergate(level - 1, x, y + 1, z);
|
||||
propergate(level - 1, x, y - 1, z);
|
||||
propergate(level - 1, x, y, z + 1);
|
||||
propergate(level - 1, x, y, z - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public List<Byte[]> getBlockLightBitMask(int chunkX, int chunkZ) {
|
||||
List<Byte[]> subchunks = new ArrayList<>(18);
|
||||
int startX = chunkX * 16;
|
||||
int endingX = startX + 16;
|
||||
int startZ = chunkZ * 16;
|
||||
int endingZ = startZ + 16;
|
||||
|
||||
for (int sub = 17; sub >= 0; sub--) {
|
||||
List<Byte> array = new ArrayList<>();
|
||||
for (int y = sub * 16; y < (sub * 16) + 16; y++) {
|
||||
for (int z = startZ; z < endingZ; z++) {
|
||||
for (int x = startX; x < endingX; x += 2) {
|
||||
int bit = blockLightArray[x][y][z];
|
||||
bit = bit << 4;
|
||||
bit |= blockLightArray[x + 1][y][z];
|
||||
array.add((byte) bit);
|
||||
}
|
||||
}
|
||||
}
|
||||
subchunks.add(array.toArray(new Byte[2048]));
|
||||
}
|
||||
|
||||
return subchunks;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
package com.loohp.limbo.World;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class LightEngineSky extends LightEngine {
|
||||
|
||||
private World world;
|
||||
private byte[][][] skyLightArray;
|
||||
|
||||
public LightEngineSky(World world) {
|
||||
skyLightArray = new byte[world.getChunkWidth() * 16][16 * 18][world.getChunkLength() * 16];
|
||||
/*
|
||||
for (byte[][] arrayarray : skyLightArray) {
|
||||
for (byte[] array : arrayarray) {
|
||||
Arrays.fill(array, (byte) 0);
|
||||
}
|
||||
}
|
||||
*/
|
||||
this.world = world;
|
||||
updateWorld();
|
||||
}
|
||||
|
||||
public void updateWorld() {
|
||||
skyLightArray = new byte[world.getChunkWidth() * 16][16 * 18][world.getChunkLength() * 16];
|
||||
for (int x = 0; x < world.getWidth(); x++) {
|
||||
for (int z = 0; z < world.getLength(); z++) {
|
||||
updateColumn(x, z);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void updateColumn(int x, int z) {
|
||||
for (int y = 272; y >= 256; y--) {
|
||||
propergate(15, x, y, z);
|
||||
}
|
||||
for (int y = 255; y >= 0; y--) {
|
||||
BlockState block = world.getBlock(x, y, z);
|
||||
//System.out.println("X:" + x + " Y: " + y + " Z: " + z + " Block: " + block.getType().toString());
|
||||
if (!block.getType().toString().equals("minecraft:air")) {
|
||||
break;
|
||||
}
|
||||
propergate(15, x, y, z);
|
||||
}
|
||||
}
|
||||
|
||||
private void propergate(int level, int x, int y, int z) {
|
||||
try {
|
||||
if (skyLightArray[x][y + 16][z] < level) {
|
||||
skyLightArray[x][y + 16][z] = (byte) level;
|
||||
if (level > 1) {
|
||||
propergate(level - 1, x + 1, y, z);
|
||||
propergate(level - 1, x - 1, y, z);
|
||||
propergate(level - 1, x, y + 1, z);
|
||||
propergate(level - 1, x, y, z + 1);
|
||||
propergate(level - 1, x, y, z - 1);
|
||||
}
|
||||
}
|
||||
} catch (ArrayIndexOutOfBoundsException e) {}
|
||||
}
|
||||
|
||||
public List<Byte[]> getSkyLightBitMask(int chunkX, int chunkZ) {
|
||||
List<Byte[]> subchunks = new ArrayList<>(18);
|
||||
int startX = chunkX * 16;
|
||||
int endingX = startX + 16;
|
||||
int startZ = chunkZ * 16;
|
||||
int endingZ = startZ + 16;
|
||||
|
||||
for (int sub = 17; sub >= 0; sub--) {
|
||||
List<Byte> array = new ArrayList<>();
|
||||
for (int y = sub * 16; y < (sub * 16) + 16; y++) {
|
||||
for (int z = startZ; z < endingZ; z++) {
|
||||
for (int x = startX; x < endingX; x += 2) {
|
||||
int bit = skyLightArray[x][y][z];
|
||||
bit = bit << 4;
|
||||
bit |= skyLightArray[x + 1][y][z];
|
||||
array.add((byte) bit);
|
||||
}
|
||||
}
|
||||
}
|
||||
subchunks.add(array.toArray(new Byte[2048]));
|
||||
}
|
||||
|
||||
return subchunks;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
package com.loohp.limbo.World;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
|
||||
import com.loohp.limbo.Utils.SchematicConvertionUtils;
|
||||
|
||||
import net.querz.mca.Chunk;
|
||||
import net.querz.nbt.tag.CompoundTag;
|
||||
import net.querz.nbt.tag.ListTag;
|
||||
|
||||
|
||||
public class Schematic {
|
||||
|
||||
public static World toWorld(String name, Environment environment, CompoundTag nbt) {
|
||||
short width = nbt.getShort("Width");
|
||||
short length = nbt.getShort("Length");
|
||||
//short height = nbt.getShort("Height");
|
||||
byte[] blockdata = nbt.getByteArray("BlockData");
|
||||
CompoundTag palette = nbt.getCompoundTag("Palette");
|
||||
ListTag<CompoundTag> blockEntities = nbt.getListTag("BlockEntities").asTypedList(CompoundTag.class);
|
||||
Map<Integer, String> mapping = new HashMap<>();
|
||||
for (String key : palette.keySet()) {
|
||||
mapping.put(palette.getInt(key), key);
|
||||
}
|
||||
|
||||
World world = new World(name, width, length, environment);
|
||||
|
||||
int index = 0;
|
||||
int i = 0;
|
||||
int value = 0;
|
||||
int varint_length = 0;
|
||||
while (i < blockdata.length) {
|
||||
value = 0;
|
||||
varint_length = 0;
|
||||
|
||||
while (true) {
|
||||
value |= (blockdata[i] & 127) << (varint_length++ * 7);
|
||||
if (varint_length > 5) {
|
||||
throw new RuntimeException("VarInt too big (probably corrupted data)");
|
||||
}
|
||||
if ((blockdata[i] & 128) != 128) {
|
||||
i++;
|
||||
break;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
// index = (y * length + z) * width + x
|
||||
int y = index / (width * length);
|
||||
int z = (index % (width * length)) / width;
|
||||
int x = (index % (width * length)) % width;
|
||||
world.setBlock(x, y, z, mapping.get(value));
|
||||
|
||||
Chunk chunk = world.getChunkAtWorldPos(x, z);
|
||||
|
||||
Iterator<CompoundTag> itr = blockEntities.iterator();
|
||||
while (itr.hasNext()) {
|
||||
CompoundTag tag = itr.next();
|
||||
int[] pos = tag.getIntArray("Pos");
|
||||
|
||||
if (pos[0] == x && pos[1] == y && pos[2] == z) {
|
||||
ListTag<CompoundTag> newTag = chunk.getTileEntities();
|
||||
newTag.add(SchematicConvertionUtils.toTileEntityTag(tag));
|
||||
chunk.setTileEntities(newTag);
|
||||
itr.remove();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
index++;
|
||||
}
|
||||
|
||||
for (Chunk[] chunkarray : world.getChunks()) {
|
||||
for (Chunk chunk : chunkarray) {
|
||||
if (chunk != null) {
|
||||
CompoundTag heightMap = new CompoundTag();
|
||||
heightMap.putLongArray("MOTION_BLOCKING", new long[] {1371773531765642314L,1389823183635651148L,1371738278539598925L,1389823183635388492L,1353688558756731469L,1389823114781694027L,1317765589597723213L,1371773531899860042L,1389823183635651149L,1371773462911685197L,1389823183635650636L,1353688626805119565L,1371773531900123211L,1335639250618849869L,1371738278674077258L,1389823114781694028L,1353723811310638154L,1371738278674077259L,1335674228429068364L,1335674228429067338L,1335674228698027594L,1317624576693539402L,1335709481520370249L,1299610178184057417L,1335638906349064264L,1299574993811968586L,1299574924958011464L,1299610178184056904L,1299574924958011464L,1299610109330100296L,1299574924958011464L,1299574924823793736L,1299574924958011465L,1281525273222484040L,1299574924958011464L,1281525273222484040L,9548107335L});
|
||||
chunk.setHeightMaps(heightMap);
|
||||
chunk.setBiomes(new int[256]);
|
||||
chunk.cleanupPalettesAndBlockStates();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
world.getLightEngineBlock().updateWorld();
|
||||
if (world.hasSkyLight()) {
|
||||
world.getLightEngineSky().updateWorld();
|
||||
}
|
||||
|
||||
return world;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,172 @@
|
||||
package com.loohp.limbo.World;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import com.loohp.limbo.Utils.SchematicConvertionUtils;
|
||||
|
||||
import net.querz.mca.Chunk;
|
||||
import net.querz.nbt.tag.CompoundTag;
|
||||
import net.querz.nbt.tag.ListTag;
|
||||
|
||||
public class World {
|
||||
|
||||
private String name;
|
||||
private Environment environment;
|
||||
private Chunk[][] chunks;
|
||||
private int width;
|
||||
private int length;
|
||||
private LightEngineBlock lightEngineBlock;
|
||||
private LightEngineSky lightEngineSky;
|
||||
|
||||
public World(String name, int width, int length, Environment environment) {
|
||||
this.name = name;
|
||||
this.environment = environment;
|
||||
this.chunks = new Chunk[(width >> 4) + 1][(length >> 4) + 1];
|
||||
this.width = width;
|
||||
this.length = length;
|
||||
|
||||
for (int x = 0; x < chunks.length; x++) {
|
||||
for (int z = 0; z < chunks[x].length; z++) {
|
||||
chunks[x][z] = Chunk.newChunk();
|
||||
Chunk chunk = chunks[x][z];
|
||||
chunk.cleanupPalettesAndBlockStates();
|
||||
CompoundTag heightMap = new CompoundTag();
|
||||
heightMap.putLongArray("MOTION_BLOCKING",
|
||||
new long[] {1371773531765642314L, 1389823183635651148L, 1371738278539598925L,
|
||||
1389823183635388492L, 1353688558756731469L, 1389823114781694027L, 1317765589597723213L,
|
||||
1371773531899860042L, 1389823183635651149L, 1371773462911685197L, 1389823183635650636L,
|
||||
1353688626805119565L, 1371773531900123211L, 1335639250618849869L, 1371738278674077258L,
|
||||
1389823114781694028L, 1353723811310638154L, 1371738278674077259L, 1335674228429068364L,
|
||||
1335674228429067338L, 1335674228698027594L, 1317624576693539402L, 1335709481520370249L,
|
||||
1299610178184057417L, 1335638906349064264L, 1299574993811968586L, 1299574924958011464L,
|
||||
1299610178184056904L, 1299574924958011464L, 1299610109330100296L, 1299574924958011464L,
|
||||
1299574924823793736L, 1299574924958011465L, 1281525273222484040L, 1299574924958011464L,
|
||||
1281525273222484040L, 9548107335L});
|
||||
chunk.setHeightMaps(heightMap);
|
||||
chunk.setBiomes(new int[256]);
|
||||
chunk.setTileEntities(new ListTag<CompoundTag>(CompoundTag.class));
|
||||
}
|
||||
}
|
||||
|
||||
this.lightEngineBlock = new LightEngineBlock(this);
|
||||
if (environment.hasSkyLight()) {
|
||||
this.lightEngineSky = new LightEngineSky(this);
|
||||
}
|
||||
}
|
||||
|
||||
public LightEngineBlock getLightEngineBlock() {
|
||||
return lightEngineBlock;
|
||||
}
|
||||
|
||||
public LightEngineSky getLightEngineSky() {
|
||||
return lightEngineSky;
|
||||
}
|
||||
|
||||
public boolean hasSkyLight() {
|
||||
return lightEngineSky != null;
|
||||
}
|
||||
|
||||
protected void setBlock(int x, int y, int z, String blockdata) {
|
||||
Chunk chunk = this.chunks[(x >> 4)][(z >> 4)];
|
||||
if (chunk == null) {
|
||||
chunk = Chunk.newChunk();
|
||||
this.chunks[(x >> 4)][(z >> 4)] = chunk;
|
||||
}
|
||||
CompoundTag block = SchematicConvertionUtils.toBlockTag(blockdata);
|
||||
chunk.setBlockStateAt(x, y, z, block, false);
|
||||
}
|
||||
|
||||
public BlockState getBlock(int x, int y, int z) {
|
||||
Chunk chunk = this.chunks[(x >> 4)][(z >> 4)];
|
||||
if (chunk == null) {
|
||||
chunk = Chunk.newChunk();
|
||||
this.chunks[(x >> 4)][(z >> 4)] = chunk;
|
||||
}
|
||||
|
||||
CompoundTag tag = chunk.getBlockStateAt(x, y, z);
|
||||
if (tag == null) {
|
||||
tag = new CompoundTag();
|
||||
tag.putString("Name", "minecraft:air");
|
||||
}
|
||||
return new BlockState(tag);
|
||||
}
|
||||
|
||||
public void setBlock(int x, int y, int z, BlockState state) {
|
||||
Chunk chunk = this.chunks[(x >> 4)][(z >> 4)];
|
||||
if (chunk == null) {
|
||||
chunk = Chunk.newChunk();
|
||||
this.chunks[(x >> 4)][(z >> 4)] = chunk;
|
||||
}
|
||||
chunk.setBlockStateAt(x % 16, y % 16, z % 16, state.toCompoundTag(), false);
|
||||
}
|
||||
|
||||
public Chunk[][] getChunks() {
|
||||
return this.chunks;
|
||||
}
|
||||
|
||||
public Chunk getChunkAtWorldPos(int x, int z) {
|
||||
return this.chunks[(x >> 4)][(z >> 4)];
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public Environment getEnvironment() {
|
||||
return environment;
|
||||
}
|
||||
|
||||
public int getWidth() {
|
||||
return width;
|
||||
}
|
||||
|
||||
public int getLength() {
|
||||
return length;
|
||||
}
|
||||
|
||||
public int getChunkWidth() {
|
||||
return (width >> 4) + 1;
|
||||
}
|
||||
|
||||
public int getChunkLength() {
|
||||
return (length >> 4) + 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + Arrays.deepHashCode(chunks);
|
||||
result = prime * result + ((environment == null) ? 0 : environment.hashCode());
|
||||
result = prime * result + ((name == null) ? 0 : name.hashCode());
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
if (getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
World other = (World) obj;
|
||||
if (!Arrays.deepEquals(chunks, other.chunks)) {
|
||||
return false;
|
||||
}
|
||||
if (environment != other.environment) {
|
||||
return false;
|
||||
}
|
||||
if (name == null) {
|
||||
if (other.name != null) {
|
||||
return false;
|
||||
}
|
||||
} else if (!name.equals(other.name)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user