forked from BLOCKFANTASY/LOOHP-Limbo
410 lines
14 KiB
Java
410 lines
14 KiB
Java
package com.loohp.limbo;
|
|
|
|
import java.awt.GraphicsEnvironment;
|
|
import java.io.File;
|
|
import java.io.FileOutputStream;
|
|
import java.io.FileReader;
|
|
import java.io.IOException;
|
|
import java.io.InputStream;
|
|
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.HashMap;
|
|
import java.util.HashSet;
|
|
import java.util.List;
|
|
import java.util.Map;
|
|
import java.util.Set;
|
|
import java.util.UUID;
|
|
import java.util.concurrent.TimeUnit;
|
|
import java.util.concurrent.atomic.AtomicInteger;
|
|
|
|
import org.json.simple.JSONObject;
|
|
import org.json.simple.parser.JSONParser;
|
|
import org.json.simple.parser.ParseException;
|
|
|
|
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.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.Schematic;
|
|
import com.loohp.limbo.World.World;
|
|
import com.loohp.limbo.World.World.Environment;
|
|
|
|
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;
|
|
}
|
|
|
|
//===========================
|
|
|
|
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;
|
|
|
|
public AtomicInteger entityIdCount = new AtomicInteger();
|
|
|
|
@SuppressWarnings("unchecked")
|
|
public Limbo() throws IOException, ParseException, NumberFormatException, ClassNotFoundException, InterruptedException {
|
|
instance = this;
|
|
|
|
if (!noGui) {
|
|
while (!GUI.loadFinish) {
|
|
TimeUnit.MILLISECONDS.sleep(500);
|
|
}
|
|
}
|
|
|
|
console = new Console(System.in, System.out, System.err);
|
|
|
|
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!");
|
|
}
|
|
|
|
String mappingName = "mapping.json";
|
|
File mappingFile = new File(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!");
|
|
|
|
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();
|
|
System.out.println("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);
|
|
|
|
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());
|
|
|
|
console.run();
|
|
}
|
|
|
|
public EventsManager getEventsManager() {
|
|
return eventsManager;
|
|
}
|
|
|
|
public PermissionsManager getPermissionsManager() {
|
|
return permissionManager;
|
|
}
|
|
|
|
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 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;
|
|
}
|
|
|
|
public String getServerListResponseJson() throws IOException {
|
|
String base = ServerProperties.JSON_BASE_RESPONSE;
|
|
base = base.replace("%VERSION%", properties.getVersionString());
|
|
base = base.replace("%PROTOCOL%", String.valueOf(properties.getProtocol()));
|
|
base = base.replace("%MOTD%", properties.getMotdJson());
|
|
base = base.replace("%MAXPLAYERS%", String.valueOf(properties.getMaxPlayers()));
|
|
base = base.replace("%ONLINECLIENTS%", String.valueOf(getPlayers().size()));
|
|
|
|
if (properties.getFavicon().isPresent()) {
|
|
String icon = "\"favicon\":\"data:image/png;base64," + ImageUtils.imgToBase64String(properties.getFavicon().get(), "png") + "\",";
|
|
base = base.replace("%FAVICON%", icon);
|
|
} else {
|
|
base = base.replace("%FAVICON%", "");
|
|
}
|
|
|
|
return base;
|
|
}
|
|
|
|
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.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();
|
|
}
|
|
}
|
|
|
|
}
|