diff --git a/pom.xml b/pom.xml index 0b08ec8..9deddf1 100644 --- a/pom.xml +++ b/pom.xml @@ -24,7 +24,7 @@ com.loohp Limbo Limbo - 0.7.7-ALPHA + 0.7.8-ALPHA Standalone Limbo Minecraft Server. https://github.com/LOOHP/Limbo @@ -260,31 +260,31 @@ net.kyori adventure-text-serializer-gson - 4.15.0-SNAPSHOT + 4.17.0-SNAPSHOT compile net.kyori adventure-text-serializer-legacy - 4.15.0-SNAPSHOT + 4.17.0-SNAPSHOT compile net.kyori adventure-text-serializer-plain - 4.15.0-SNAPSHOT + 4.17.0-SNAPSHOT compile net.kyori adventure-api - 4.15.0-SNAPSHOT + 4.17.0-SNAPSHOT compile net.kyori adventure-nbt - 4.15.0-SNAPSHOT + 4.17.0-SNAPSHOT compile diff --git a/src/main/java/com/loohp/limbo/Limbo.java b/src/main/java/com/loohp/limbo/Limbo.java index 4ff2524..2c48383 100644 --- a/src/main/java/com/loohp/limbo/Limbo.java +++ b/src/main/java/com/loohp/limbo/Limbo.java @@ -98,566 +98,553 @@ import java.util.stream.Collectors; public final class Limbo { - public static final String LIMBO_BRAND = "Limbo"; + public static final String LIMBO_BRAND = "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") || flag.equals("nogui")) { - noGui = true; - } else if (flag.equals("--help")) { - System.out.println("Accepted flags:"); - System.out.println(" --nogui <- Disable the GUI"); - System.exit(0); - } else { - System.out.println("Unknown flag: \"" + flag + "\". Ignoring..."); - } - } - if (GraphicsEnvironment.isHeadless()) { - noGui = true; - } - if (!noGui) { - System.out.println("Launching Server GUI.. Add \"--nogui\" in launch arguments to disable"); - Thread t1 = new Thread(() -> { - try { - GUI.main(); - } catch (UnsupportedLookAndFeelException | ClassNotFoundException | InstantiationException | - IllegalAccessException e) { - e.printStackTrace(); - } - }); - t1.start(); - } - - new Limbo(); - } - - public static Limbo getInstance() { - return instance; - } - - //=========================== - - public final String SERVER_IMPLEMENTATION_VERSION = "1.20.4"; - public final int SERVER_IMPLEMENTATION_PROTOCOL = 765; - public final String LIMBO_IMPLEMENTATION_VERSION; - - private final AtomicBoolean isRunning; - - private final ServerConnection server; - private final Console console; - - private final List worlds = new CopyOnWriteArrayList<>(); - final Map playersByName = new ConcurrentHashMap<>(); - final Map playersByUUID = new ConcurrentHashMap<>(); - private final Map bossBars = new ConcurrentHashMap<>(); - - private final ServerProperties properties; - - private final PluginManager pluginManager; - private final EventsManager eventsManager; - private final PermissionsManager permissionManager; - private final File pluginFolder; - - private final File internalDataFolder; - - private final DimensionRegistry dimensionRegistry; - - private final Tick tick; - private final LimboScheduler scheduler; - - private final Metrics metrics; - - public final AtomicInteger entityIdCount = new AtomicInteger(); - - @SuppressWarnings("deprecation") - private Unsafe unsafe; - - @SuppressWarnings("unchecked") - public Limbo() throws IOException, ParseException, NumberFormatException, ClassNotFoundException, InterruptedException { - instance = this; - unsafe = new Unsafe(this); - isRunning = new AtomicBoolean(true); - - 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); - } - - LIMBO_IMPLEMENTATION_VERSION = getLimboVersion(); - console.sendMessage("Loading Limbo Version " + LIMBO_IMPLEMENTATION_VERSION + " on Minecraft " + SERVER_IMPLEMENTATION_VERSION); - - String spName = "server.properties"; + 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") || flag.equals("nogui")) { + noGui = true; + } else if (flag.equals("--help")) { + System.out.println("Accepted flags:"); + System.out.println(" --nogui <- Disable the GUI"); + System.exit(0); + } else { + System.out.println("Unknown flag: \"" + flag + "\". Ignoring..."); + } + } + if (GraphicsEnvironment.isHeadless()) { + noGui = true; + } + if (!noGui) { + System.out.println("Launching Server GUI.. Add \"--nogui\" in launch arguments to disable"); + Thread t1 = new Thread(() -> { + try { + GUI.main(); + } catch (UnsupportedLookAndFeelException | ClassNotFoundException | InstantiationException | IllegalAccessException e) { + e.printStackTrace(); + } + }); + t1.start(); + } + + new Limbo(); + } + + public static Limbo getInstance() { + return instance; + } + + //=========================== + + public final String SERVER_IMPLEMENTATION_VERSION = "1.20.4"; + public final int SERVER_IMPLEMENTATION_PROTOCOL = 765; + public final String LIMBO_IMPLEMENTATION_VERSION; + + private final AtomicBoolean isRunning; + + private final ServerConnection server; + private final Console console; + + private final List worlds = new CopyOnWriteArrayList<>(); + final Map playersByName = new ConcurrentHashMap<>(); + final Map playersByUUID = new ConcurrentHashMap<>(); + private final Map bossBars = new ConcurrentHashMap<>(); + + private final ServerProperties properties; + + private final PluginManager pluginManager; + private final EventsManager eventsManager; + private final PermissionsManager permissionManager; + private final File pluginFolder; + + private final DimensionRegistry dimensionRegistry; + + private final Tick tick; + private final LimboScheduler scheduler; + + private final Metrics metrics; + + public final AtomicInteger entityIdCount = new AtomicInteger(); + + @SuppressWarnings("deprecation") + private Unsafe unsafe; + + @SuppressWarnings("unchecked") + public Limbo() throws IOException, ParseException, NumberFormatException, ClassNotFoundException, InterruptedException { + instance = this; + unsafe = new Unsafe(this); + isRunning = new AtomicBoolean(true); + + 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); + } + + LIMBO_IMPLEMENTATION_VERSION = getLimboVersion(); + console.sendMessage("Loading Limbo Version " + LIMBO_IMPLEMENTATION_VERSION + " on Minecraft " + SERVER_IMPLEMENTATION_VERSION); + + String spName = "server.properties"; File sp = new File(spName); if (!sp.exists()) { - try (InputStream in = getClass().getClassLoader().getResourceAsStream(spName)) { + try (InputStream in = getClass().getClassLoader().getResourceAsStream(spName)) { Files.copy(in, sp.toPath()); } catch (IOException e) { - e.printStackTrace(); + e.printStackTrace(); } } properties = new ServerProperties(sp); - + if (!properties.isBungeecord()) { - console.sendMessage("If you are using bungeecord, consider turning that on in the settings!"); + console.sendMessage("If you are using bungeecord, consider turning that on in the settings!"); } else { - console.sendMessage("Starting Limbo server in bungeecord mode!"); + console.sendMessage("Starting Limbo server in bungeecord mode!"); } + + String mappingName = "mapping.json"; + InputStream mappingStream = getClass().getClassLoader().getResourceAsStream(mappingName); + if (mappingStream == null) { + throw new RuntimeException("Failed to load " + mappingName + " from jar!"); + } + + console.sendMessage("Loading packet id mappings..."); - internalDataFolder = new File("internal_data"); - if (!internalDataFolder.exists()) { - internalDataFolder.mkdirs(); - } - - console.sendMessage("Loading packet id mappings from mapping.json ..."); - - InputStream mappingStream = getClass().getClassLoader().getResourceAsStream("mapping.json"); - if (mappingStream == null) { - console.sendMessage("Failed to load mapping.json from jar!"); - System.exit(1); - } - - InputStreamReader reader = new InputStreamReader(mappingStream, StandardCharsets.UTF_8); + InputStreamReader reader = new InputStreamReader(mappingStream, StandardCharsets.UTF_8); JSONObject json = (JSONObject) new JSONParser().parse(reader); reader.close(); - + String classPrefix = Packet.class.getName().substring(0, Packet.class.getName().lastIndexOf(".") + 1); int mappingsCount = 0; + + Map> HandshakeIn = new HashMap<>(); + for (Object key : ((JSONObject) json.get("HandshakeIn")).keySet()) { + int packetId = Integer.decode((String) key); + HandshakeIn.put(packetId, (Class) Class.forName(classPrefix + ((JSONObject) json.get("HandshakeIn")).get(key))); + } + Packet.setHandshakeIn(HandshakeIn); + mappingsCount += HandshakeIn.size(); + + Map> StatusIn = new HashMap<>(); + for (Object key : ((JSONObject) json.get("StatusIn")).keySet()) { + int packetId = Integer.decode((String) key); + StatusIn.put(packetId, (Class) Class.forName(classPrefix + ((JSONObject) json.get("StatusIn")).get(key))); + } + Packet.setStatusIn(StatusIn); + mappingsCount += StatusIn.size(); + + Map, Integer> StatusOut = new HashMap<>(); + for (Object key : ((JSONObject) json.get("StatusOut")).keySet()) { + Class packetClass = (Class) Class.forName(classPrefix + key); + StatusOut.put(packetClass, Integer.decode((String) ((JSONObject) json.get("StatusOut")).get(key))); + } + Packet.setStatusOut(StatusOut); + mappingsCount += StatusOut.size(); + + Map> LoginIn = new HashMap<>(); + for (Object key : ((JSONObject) json.get("LoginIn")).keySet()) { + int packetId = Integer.decode((String) key); + LoginIn.put(packetId, (Class) Class.forName(classPrefix + ((JSONObject) json.get("LoginIn")).get(key))); + } + Packet.setLoginIn(LoginIn); + mappingsCount += LoginIn.size(); + + Map, Integer> LoginOut = new HashMap<>(); + for (Object key : ((JSONObject) json.get("LoginOut")).keySet()) { + Class packetClass = (Class) Class.forName(classPrefix + key); + LoginOut.put(packetClass, Integer.decode((String) ((JSONObject) json.get("LoginOut")).get(key))); + } + Packet.setLoginOut(LoginOut); + mappingsCount += LoginOut.size(); - Map> HandshakeIn = new HashMap<>(); - for (Object key : ((JSONObject) json.get("HandshakeIn")).keySet()) { - int packetId = Integer.decode((String) key); - HandshakeIn.put(packetId, (Class) Class.forName(classPrefix + ((JSONObject) json.get("HandshakeIn")).get(key))); - } - Packet.setHandshakeIn(HandshakeIn); - mappingsCount += HandshakeIn.size(); + Map> ConfigurationIn = new HashMap<>(); + for (Object key : ((JSONObject) json.get("ConfigurationIn")).keySet()) { + int packetId = Integer.decode((String) key); + ConfigurationIn.put(packetId, (Class) Class.forName(classPrefix + ((JSONObject) json.get("ConfigurationIn")).get(key))); + } + Packet.setConfigurationIn(ConfigurationIn); + mappingsCount += ConfigurationIn.size(); - Map> StatusIn = new HashMap<>(); - for (Object key : ((JSONObject) json.get("StatusIn")).keySet()) { - int packetId = Integer.decode((String) key); - StatusIn.put(packetId, (Class) Class.forName(classPrefix + ((JSONObject) json.get("StatusIn")).get(key))); - } - Packet.setStatusIn(StatusIn); - mappingsCount += StatusIn.size(); - - Map, Integer> StatusOut = new HashMap<>(); - for (Object key : ((JSONObject) json.get("StatusOut")).keySet()) { - Class packetClass = (Class) Class.forName(classPrefix + key); - StatusOut.put(packetClass, Integer.decode((String) ((JSONObject) json.get("StatusOut")).get(key))); - } - Packet.setStatusOut(StatusOut); - mappingsCount += StatusOut.size(); - - Map> LoginIn = new HashMap<>(); - for (Object key : ((JSONObject) json.get("LoginIn")).keySet()) { - int packetId = Integer.decode((String) key); - LoginIn.put(packetId, (Class) Class.forName(classPrefix + ((JSONObject) json.get("LoginIn")).get(key))); - } - Packet.setLoginIn(LoginIn); - mappingsCount += LoginIn.size(); - - Map, Integer> LoginOut = new HashMap<>(); - for (Object key : ((JSONObject) json.get("LoginOut")).keySet()) { - Class packetClass = (Class) Class.forName(classPrefix + key); - LoginOut.put(packetClass, Integer.decode((String) ((JSONObject) json.get("LoginOut")).get(key))); - } - Packet.setLoginOut(LoginOut); - mappingsCount += LoginOut.size(); - - Map> ConfigurationIn = new HashMap<>(); - for (Object key : ((JSONObject) json.get("ConfigurationIn")).keySet()) { - int packetId = Integer.decode((String) key); - ConfigurationIn.put(packetId, (Class) Class.forName(classPrefix + ((JSONObject) json.get("ConfigurationIn")).get(key))); - } - Packet.setConfigurationIn(ConfigurationIn); - mappingsCount += ConfigurationIn.size(); - - Map, Integer> ConfigurationOut = new HashMap<>(); - for (Object key : ((JSONObject) json.get("ConfigurationOut")).keySet()) { - Class packetClass = (Class) Class.forName(classPrefix + key); - ConfigurationOut.put(packetClass, Integer.decode((String) ((JSONObject) json.get("ConfigurationOut")).get(key))); - } - Packet.setConfigurationOut(ConfigurationOut); - mappingsCount += ConfigurationOut.size(); - - Map> PlayIn = new HashMap<>(); - for (Object key : ((JSONObject) json.get("PlayIn")).keySet()) { - int packetId = Integer.decode((String) key); - PlayIn.put(packetId, (Class) Class.forName(classPrefix + ((JSONObject) json.get("PlayIn")).get(key))); - } - Packet.setPlayIn(PlayIn); - mappingsCount += PlayIn.size(); - - Map, Integer> PlayOut = new HashMap<>(); - for (Object key : ((JSONObject) json.get("PlayOut")).keySet()) { - Class packetClass = (Class) Class.forName(classPrefix + 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().value()), 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"; + Map, Integer> ConfigurationOut = new HashMap<>(); + for (Object key : ((JSONObject) json.get("ConfigurationOut")).keySet()) { + Class packetClass = (Class) Class.forName(classPrefix + key); + ConfigurationOut.put(packetClass, Integer.decode((String) ((JSONObject) json.get("ConfigurationOut")).get(key))); + } + Packet.setConfigurationOut(ConfigurationOut); + mappingsCount += ConfigurationOut.size(); + + Map> PlayIn = new HashMap<>(); + for (Object key : ((JSONObject) json.get("PlayIn")).keySet()) { + int packetId = Integer.decode((String) key); + PlayIn.put(packetId, (Class) Class.forName(classPrefix + ((JSONObject) json.get("PlayIn")).get(key))); + } + Packet.setPlayIn(PlayIn); + mappingsCount += PlayIn.size(); + + Map, Integer> PlayOut = new HashMap<>(); + for (Object key : ((JSONObject) json.get("PlayOut")).keySet()) { + Class packetClass = (Class) Class.forName(classPrefix + 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().value()), 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)) { + try (InputStream in = getClass().getClassLoader().getResourceAsStream(permissionName)) { Files.copy(in, permissionFile.toPath()); } catch (IOException e) { - e.printStackTrace(); + e.printStackTrace(); } } scheduler = new LimboScheduler(); - tick = new Tick(this); - + tick = new Tick(this); + permissionManager = new PermissionsManager(); - permissionManager.loadDefaultPermissionFile(permissionFile); - + permissionManager.loadDefaultPermissionFile(permissionFile); + eventsManager = new EventsManager(); - + pluginFolder = new File("plugins"); pluginFolder.mkdirs(); + + pluginManager = new PluginManager(new DefaultCommands(), 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()) { + try { + console.sendMessage("Enabling plugin " + plugin.getName() + " " + plugin.getInfo().getVersion()); + plugin.onEnable(); + } catch (Throwable e) { + new RuntimeException("Error while enabling " + plugin.getName() + " " + plugin.getInfo().getVersion(), e).printStackTrace(); + } + } + + server = new ServerConnection(properties.getServerIp(), properties.getServerPort()); + + metrics = new Metrics(); + + Runtime.getRuntime().addShutdownHook(new Thread(() -> { + Limbo.getInstance().terminate(); + })); - pluginManager = new PluginManager(new DefaultCommands(), 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(); - } + console.run(); + } - for (LimboPlugin plugin : Limbo.getInstance().getPluginManager().getPlugins()) { - try { - console.sendMessage("Enabling plugin " + plugin.getName() + " " + plugin.getInfo().getVersion()); - plugin.onEnable(); - } catch (Throwable e) { - new RuntimeException("Error while enabling " + plugin.getName() + " " + plugin.getInfo().getVersion(), e).printStackTrace(); - } - } + @Deprecated + public Unsafe getUnsafe() { + return unsafe; + } + + public Tick getHeartBeat() { + return tick; + } + + public LimboScheduler getScheduler() { + return scheduler; + } - server = new ServerConnection(properties.getServerIp(), properties.getServerPort()); + public DimensionRegistry getDimensionRegistry() { + return dimensionRegistry; + } - metrics = new Metrics(); + public PermissionsManager getPermissionsManager() { + return permissionManager; + } - Runtime.getRuntime().addShutdownHook(new Thread(() -> { - Limbo.getInstance().terminate(); - })); + public EventsManager getEventsManager() { + return eventsManager; + } + + public File getPluginFolder() { + return pluginFolder; + } + + public PluginManager getPluginManager() { + return pluginManager; + } - console.run(); - } + 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("Creating default world..."); + try (InputStream in = Limbo.class.getClassLoader().getResourceAsStream("spawn.schem")) { + Files.copy(in, schem.toPath()); + } catch (IOException e) { + e.printStackTrace(); + } + } + + try { + World world = Schematic.toWorld(properties.getLevelName().value(), Environment.fromKey(properties.getLevelDimension()), (CompoundTag) NBTUtil.read(schem).getTag()); + console.sendMessage("Loaded world " + properties.getLevelName() + "!"); + return world; + } catch (Throwable e) { + console.sendMessage("Unable to load world " + properties.getSchemFileName() + "!"); + e.printStackTrace(); + console.sendMessage("Server will exit!"); + System.exit(1); + return null; + } + } + + public void registerWorld(World world) { + if (!worlds.contains(world)) { + worlds.add(world); + } else { + throw new RuntimeException("World already registered"); + } + } + + public void unregisterWorld(World world) { + if (worlds.indexOf(world) == 0) { + throw new RuntimeException("World already registered"); + } else if (!worlds.contains(world)) { + throw new RuntimeException("World not registered"); + } else { + for (Player player : world.getPlayers()) { + player.teleport(properties.getWorldSpawn()); + } + worlds.remove(world); + } + } - @Deprecated - public Unsafe getUnsafe() { - return unsafe; - } + public KeyedBossBar createBossBar(Key Key, Component name, float progress, BossBar.Color color, BossBar.Overlay overlay, BossBar.Flag... flags) { + KeyedBossBar keyedBossBar = com.loohp.limbo.bossbar.Unsafe.a(Key, BossBar.bossBar(name, progress, color, overlay, new HashSet<>(Arrays.asList(flags)))); + bossBars.put(Key, keyedBossBar); + return keyedBossBar; + } - public Tick getHeartBeat() { - return tick; - } + public void removeBossBar(Key Key) { + KeyedBossBar keyedBossBar = bossBars.remove(Key); + keyedBossBar.getProperties().removeListener(keyedBossBar.getUnsafe().a()); + keyedBossBar.getUnsafe().b(); + PacketPlayOutBoss packetPlayOutBoss = new PacketPlayOutBoss(keyedBossBar, PacketPlayOutBoss.BossBarAction.REMOVE); + for (Player player : keyedBossBar.getPlayers()) { + try { + player.clientConnection.sendPacket(packetPlayOutBoss); + } catch (IOException e) { + e.printStackTrace(); + } + } + } - public LimboScheduler getScheduler() { - return scheduler; - } + public Map getBossBars() { + return Collections.unmodifiableMap(bossBars); + } - public DimensionRegistry getDimensionRegistry() { - return dimensionRegistry; - } + public ServerProperties getServerProperties() { + return properties; + } + + public ServerConnection getServerConnection() { + return server; + } - public PermissionsManager getPermissionsManager() { - return permissionManager; - } + public Console getConsole() { + return console; + } + + public Metrics getMetrics() { + return metrics; + } - public File getInternalDataFolder() { - return internalDataFolder; - } + public Set 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 List 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, Component motd, int maxPlayers, int playersOnline, BufferedImage favicon) throws IOException { + JSONObject json = new JSONObject(); - public EventsManager getEventsManager() { - return eventsManager; - } + 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 treeMap = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); + treeMap.putAll(json); + + Gson g = new GsonBuilder().create(); - public File getPluginFolder() { - return pluginFolder; - } + return g.toJson(treeMap).replace("\"%MOTD%\"", GsonComponentSerializer.gson().serialize(motd)); + } + + public String buildLegacyPingResponse(String version, Component motd, int maxPlayers, int playersOnline) { + String begin = "�1"; + return String.join("\00", begin, "127", version, String.join("", Arrays.asList(motd).stream().map(each -> LegacyComponentSerializer.legacySection().serialize(each)).collect(Collectors.toList())), String.valueOf(playersOnline), String.valueOf(maxPlayers)); + } + + protected void terminate() { + isRunning.set(false); + console.sendMessage("Stopping Server..."); + + for (LimboPlugin plugin : Limbo.getInstance().getPluginManager().getPlugins()) { + try { + console.sendMessage("Disabling plugin " + plugin.getName() + " " + plugin.getInfo().getVersion()); + plugin.onDisable(); + } catch (Throwable e) { + new RuntimeException("Error while disabling " + plugin.getName() + " " + plugin.getInfo().getVersion(), e).printStackTrace(); + } + } + + tick.waitAndKillThreads(5000); + + 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(); + } + + public void stopServer() { + System.exit(0); + } + + public boolean isRunning() { + return isRunning.get(); + } + + public int getNextEntityId() { + return entityIdCount.getAndUpdate(i -> i == Integer.MAX_VALUE ? 0 : ++i); + } + + 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 manifests = getClass().getClassLoader().getResources("META-INF/MANIFEST.MF"); + while (manifests.hasMoreElements()) { + URL url = manifests.nextElement(); + try (BufferedReader br = new BufferedReader(new InputStreamReader(url.openStream()))) { + Optional line = br.lines().filter(each -> each.startsWith("Limbo-Version:")).findFirst(); + if (line.isPresent()) { + return line.get().substring(14).trim(); + } + } + } + return "Unknown"; + } - public PluginManager getPluginManager() { - return pluginManager; - } + public Inventory createInventory(Component title, int slots, InventoryHolder holder) { + return CustomInventory.create(title, slots, holder); + } - private World loadDefaultWorld() throws IOException { - console.sendMessage("Loading world " + properties.getLevelName() + " with the schematic file " + properties.getSchemFileName() + " ..."); + public Inventory createInventory(InventoryType type, InventoryHolder holder) { + return createInventory(null, type, holder); + } - File schem = new File(properties.getSchemFileName()); - - if (!schem.exists()) { - console.sendMessage("Schemetic file " + properties.getSchemFileName() + " for world " + properties.getLevelName() + " not found!"); - console.sendMessage("Creating default world..."); - try (InputStream in = Limbo.class.getClassLoader().getResourceAsStream("spawn.schem")) { - Files.copy(in, schem.toPath()); - } catch (IOException e) { - e.printStackTrace(); - } - } - - try { - World world = Schematic.toWorld(properties.getLevelName().value(), Environment.fromKey(properties.getLevelDimension()), (CompoundTag) NBTUtil.read(schem).getTag()); - console.sendMessage("Loaded world " + properties.getLevelName() + "!"); - return world; - } catch (Throwable e) { - console.sendMessage("Unable to load world " + properties.getSchemFileName() + "!"); - e.printStackTrace(); - console.sendMessage("Server will exit!"); - System.exit(1); - return null; - } - } - - public void registerWorld(World world) { - if (!worlds.contains(world)) { - worlds.add(world); - } else { - throw new RuntimeException("World already registered"); - } - } - - public void unregisterWorld(World world) { - if (worlds.indexOf(world) == 0) { - throw new RuntimeException("World already registered"); - } else if (!worlds.contains(world)) { - throw new RuntimeException("World not registered"); - } else { - for (Player player : world.getPlayers()) { - player.teleport(properties.getWorldSpawn()); - } - worlds.remove(world); - } - } - - public KeyedBossBar createBossBar(Key Key, Component name, float progress, BossBar.Color color, BossBar.Overlay overlay, BossBar.Flag... flags) { - KeyedBossBar keyedBossBar = com.loohp.limbo.bossbar.Unsafe.a(Key, BossBar.bossBar(name, progress, color, overlay, new HashSet<>(Arrays.asList(flags)))); - bossBars.put(Key, keyedBossBar); - return keyedBossBar; - } - - public void removeBossBar(Key Key) { - KeyedBossBar keyedBossBar = bossBars.remove(Key); - keyedBossBar.getProperties().removeListener(keyedBossBar.getUnsafe().a()); - keyedBossBar.getUnsafe().b(); - PacketPlayOutBoss packetPlayOutBoss = new PacketPlayOutBoss(keyedBossBar, PacketPlayOutBoss.BossBarAction.REMOVE); - for (Player player : keyedBossBar.getPlayers()) { - try { - player.clientConnection.sendPacket(packetPlayOutBoss); - } catch (IOException e) { - e.printStackTrace(); - } - } - } - - public Map getBossBars() { - return Collections.unmodifiableMap(bossBars); - } - - public ServerProperties getServerProperties() { - return properties; - } - - public ServerConnection getServerConnection() { - return server; - } - - public Console getConsole() { - return console; - } - - public Metrics getMetrics() { - return metrics; - } - - public Set 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 List 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, Component 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 treeMap = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); - treeMap.putAll(json); - - Gson g = new GsonBuilder().create(); - - return g.toJson(treeMap).replace("\"%MOTD%\"", GsonComponentSerializer.gson().serialize(motd)); - } - - public String buildLegacyPingResponse(String version, Component motd, int maxPlayers, int playersOnline) { - String begin = "�1"; - return String.join("\00", begin, "127", version, String.join("", Arrays.asList(motd).stream().map(each -> LegacyComponentSerializer.legacySection().serialize(each)).collect(Collectors.toList())), String.valueOf(playersOnline), String.valueOf(maxPlayers)); - } - - protected void terminate() { - isRunning.set(false); - console.sendMessage("Stopping Server..."); - - for (LimboPlugin plugin : Limbo.getInstance().getPluginManager().getPlugins()) { - try { - console.sendMessage("Disabling plugin " + plugin.getName() + " " + plugin.getInfo().getVersion()); - plugin.onDisable(); - } catch (Throwable e) { - new RuntimeException("Error while disabling " + plugin.getName() + " " + plugin.getInfo().getVersion(), e).printStackTrace(); - } - } - - tick.waitAndKillThreads(5000); - - 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(); - } - - public void stopServer() { - System.exit(0); - } - - public boolean isRunning() { - return isRunning.get(); - } - - public int getNextEntityId() { - return entityIdCount.getAndUpdate(i -> i == Integer.MAX_VALUE ? 0 : ++i); - } - - 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 manifests = getClass().getClassLoader().getResources("META-INF/MANIFEST.MF"); - while (manifests.hasMoreElements()) { - URL url = manifests.nextElement(); - try (BufferedReader br = new BufferedReader(new InputStreamReader(url.openStream()))) { - Optional line = br.lines().filter(each -> each.startsWith("Limbo-Version:")).findFirst(); - if (line.isPresent()) { - return line.get().substring(14).trim(); - } - } - } - return "Unknown"; - } - - public Inventory createInventory(Component title, int slots, InventoryHolder holder) { - return CustomInventory.create(title, slots, holder); - } - - public Inventory createInventory(InventoryType type, InventoryHolder holder) { - return createInventory(null, type, holder); - } - - public Inventory createInventory(Component title, InventoryType type, InventoryHolder holder) { - if (!type.isCreatable()) { - throw new UnsupportedOperationException("This InventoryType cannot be created."); - } - switch (type) { - case ANVIL: - return new AnvilInventory(title, holder); - default: - throw new UnsupportedOperationException("This InventoryType has not been implemented yet."); - } - } + public Inventory createInventory(Component title, InventoryType type, InventoryHolder holder) { + if (!type.isCreatable()) { + throw new UnsupportedOperationException("This InventoryType cannot be created."); + } + switch (type) { + case ANVIL: + return new AnvilInventory(title, holder); + default: + throw new UnsupportedOperationException("This InventoryType has not been implemented yet."); + } + } } diff --git a/src/main/java/com/loohp/limbo/network/ClientConnection.java b/src/main/java/com/loohp/limbo/network/ClientConnection.java index 62c89e9..485b761 100644 --- a/src/main/java/com/loohp/limbo/network/ClientConnection.java +++ b/src/main/java/com/loohp/limbo/network/ClientConnection.java @@ -41,13 +41,61 @@ import com.loohp.limbo.inventory.AnvilInventory; import com.loohp.limbo.inventory.Inventory; import com.loohp.limbo.inventory.ItemStack; import com.loohp.limbo.location.Location; -import com.loohp.limbo.network.protocol.packets.*; +import com.loohp.limbo.network.protocol.packets.ClientboundFinishConfigurationPacket; +import com.loohp.limbo.network.protocol.packets.ClientboundRegistryDataPacket; +import com.loohp.limbo.network.protocol.packets.Packet; +import com.loohp.limbo.network.protocol.packets.PacketHandshakingIn; +import com.loohp.limbo.network.protocol.packets.PacketIn; +import com.loohp.limbo.network.protocol.packets.PacketLoginInLoginStart; +import com.loohp.limbo.network.protocol.packets.PacketLoginInPluginMessaging; +import com.loohp.limbo.network.protocol.packets.PacketLoginOutDisconnect; +import com.loohp.limbo.network.protocol.packets.PacketLoginOutLoginSuccess; +import com.loohp.limbo.network.protocol.packets.PacketLoginOutPluginMessaging; +import com.loohp.limbo.network.protocol.packets.PacketOut; +import com.loohp.limbo.network.protocol.packets.PacketPlayInBlockDig; +import com.loohp.limbo.network.protocol.packets.PacketPlayInBlockPlace; +import com.loohp.limbo.network.protocol.packets.PacketPlayInChat; +import com.loohp.limbo.network.protocol.packets.PacketPlayInCloseWindow; +import com.loohp.limbo.network.protocol.packets.PacketPlayInHeldItemChange; +import com.loohp.limbo.network.protocol.packets.PacketPlayInItemName; +import com.loohp.limbo.network.protocol.packets.PacketPlayInKeepAlive; +import com.loohp.limbo.network.protocol.packets.PacketPlayInPickItem; +import com.loohp.limbo.network.protocol.packets.PacketPlayInPluginMessaging; +import com.loohp.limbo.network.protocol.packets.PacketPlayInPosition; +import com.loohp.limbo.network.protocol.packets.PacketPlayInPositionAndLook; +import com.loohp.limbo.network.protocol.packets.PacketPlayOutGameStateChange; +import com.loohp.limbo.network.protocol.packets.ServerboundResourcePackPacket; import com.loohp.limbo.network.protocol.packets.ServerboundResourcePackPacket.Action; +import com.loohp.limbo.network.protocol.packets.PacketPlayInRotation; +import com.loohp.limbo.network.protocol.packets.PacketPlayInSetCreativeSlot; +import com.loohp.limbo.network.protocol.packets.PacketPlayInTabComplete; +import com.loohp.limbo.network.protocol.packets.PacketPlayInUseItem; +import com.loohp.limbo.network.protocol.packets.PacketPlayInWindowClick; +import com.loohp.limbo.network.protocol.packets.PacketPlayOutDeclareCommands; +import com.loohp.limbo.network.protocol.packets.PacketPlayOutDisconnect; +import com.loohp.limbo.network.protocol.packets.PacketPlayOutEntityMetadata; +import com.loohp.limbo.network.protocol.packets.PacketPlayOutHeldItemChange; +import com.loohp.limbo.network.protocol.packets.PacketPlayOutKeepAlive; +import com.loohp.limbo.network.protocol.packets.PacketPlayOutLogin; +import com.loohp.limbo.network.protocol.packets.PacketPlayOutPlayerAbilities; import com.loohp.limbo.network.protocol.packets.PacketPlayOutPlayerAbilities.PlayerAbilityFlags; +import com.loohp.limbo.network.protocol.packets.PacketPlayOutPlayerInfo; import com.loohp.limbo.network.protocol.packets.PacketPlayOutPlayerInfo.PlayerInfoAction; import com.loohp.limbo.network.protocol.packets.PacketPlayOutPlayerInfo.PlayerInfoData; import com.loohp.limbo.network.protocol.packets.PacketPlayOutPlayerInfo.PlayerInfoData.PlayerInfoDataAddPlayer.PlayerSkinProperty; +import com.loohp.limbo.network.protocol.packets.PacketPlayOutPluginMessaging; +import com.loohp.limbo.network.protocol.packets.PacketPlayOutPositionAndLook; +import com.loohp.limbo.network.protocol.packets.PacketPlayOutSpawnPosition; +import com.loohp.limbo.network.protocol.packets.PacketPlayOutTabComplete; import com.loohp.limbo.network.protocol.packets.PacketPlayOutTabComplete.TabCompleteMatches; +import com.loohp.limbo.network.protocol.packets.PacketPlayOutUpdateViewPosition; +import com.loohp.limbo.network.protocol.packets.PacketStatusInPing; +import com.loohp.limbo.network.protocol.packets.PacketStatusInRequest; +import com.loohp.limbo.network.protocol.packets.PacketStatusOutPong; +import com.loohp.limbo.network.protocol.packets.PacketStatusOutResponse; +import com.loohp.limbo.network.protocol.packets.ServerboundChatCommandPacket; +import com.loohp.limbo.network.protocol.packets.ServerboundFinishConfigurationPacket; +import com.loohp.limbo.network.protocol.packets.ServerboundLoginAcknowledgedPacket; import com.loohp.limbo.player.Player; import com.loohp.limbo.player.PlayerInteractManager; import com.loohp.limbo.player.PlayerInventory; @@ -565,6 +613,10 @@ public class ClientConnection extends Thread { String str = (properties.isLogPlayerIPAddresses() ? inetAddress.getHostName() : "") + ":" + clientSocket.getPort() + "|" + player.getName() + "(" + player.getUniqueId() + ")"; Limbo.getInstance().getConsole().sendMessage("[/" + str + "] <-> Player had connected to the Limbo server!"); + PacketPlayOutGameStateChange gameEvent = new PacketPlayOutGameStateChange(PacketPlayOutGameStateChange.GameStateChangeEvent.LEVEL_CHUNKS_LOAD_START, 0); + sendPacket(gameEvent); + player.playerInteractManager.update(); + PacketPlayOutDeclareCommands declare = DeclareCommands.getDeclareCommandsPacket(player); if (declare != null) { sendPacket(declare); @@ -584,7 +636,7 @@ public class ClientConnection extends Thread { Limbo.getInstance().getEventsManager().callEvent(new PlayerJoinEvent(player)); if (properties.isAllowFlight()) { - PacketPlayOutGameState state = new PacketPlayOutGameState(3, player.getGamemode().getId()); + PacketPlayOutGameStateChange state = new PacketPlayOutGameStateChange(PacketPlayOutGameStateChange.GameStateChangeEvent.CHANGE_GAME_MODE, player.getGamemode().getId()); sendPacket(state); } @@ -603,13 +655,8 @@ public class ClientConnection extends Thread { // PLAYER LIST HEADER AND FOOTER CODE CONRIBUTED BY GAMERDUCK123 player.sendPlayerListHeaderAndFooter(properties.getTabHeader(), properties.getTabFooter()); - - // Start waiting for level chunks - PacketPlayOutGameEvent gameEvent = new PacketPlayOutGameEvent((byte) 13, 0); - sendPacket(gameEvent); - + ready = true; - player.playerInteractManager.update(); keepAliveTask = new TimerTask() { @Override diff --git a/src/main/java/com/loohp/limbo/network/protocol/packets/PacketPlayOutGameState.java b/src/main/java/com/loohp/limbo/network/protocol/packets/PacketPlayOutGameState.java deleted file mode 100644 index ab61263..0000000 --- a/src/main/java/com/loohp/limbo/network/protocol/packets/PacketPlayOutGameState.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * This file is part of Limbo. - * - * Copyright (C) 2022. LoohpJames - * Copyright (C) 2022. Contributors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.loohp.limbo.network.protocol.packets; - -import java.io.ByteArrayOutputStream; -import java.io.DataOutputStream; -import java.io.IOException; - -public class PacketPlayOutGameState extends PacketOut { - - private final int reason; - private final 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(); - } - -} diff --git a/src/main/java/com/loohp/limbo/network/protocol/packets/PacketPlayOutGameEvent.java b/src/main/java/com/loohp/limbo/network/protocol/packets/PacketPlayOutGameStateChange.java similarity index 55% rename from src/main/java/com/loohp/limbo/network/protocol/packets/PacketPlayOutGameEvent.java rename to src/main/java/com/loohp/limbo/network/protocol/packets/PacketPlayOutGameStateChange.java index 276b414..74ab37b 100644 --- a/src/main/java/com/loohp/limbo/network/protocol/packets/PacketPlayOutGameEvent.java +++ b/src/main/java/com/loohp/limbo/network/protocol/packets/PacketPlayOutGameStateChange.java @@ -1,8 +1,8 @@ /* * This file is part of Limbo. * - * Copyright (C) 2022. LoohpJames - * Copyright (C) 2022. Contributors + * Copyright (C) 2024. LoohpJames + * Copyright (C) 2024. Contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -23,17 +23,44 @@ import java.io.ByteArrayOutputStream; import java.io.DataOutputStream; import java.io.IOException; -public class PacketPlayOutGameEvent extends PacketOut { +public class PacketPlayOutGameStateChange extends PacketOut { - private byte event; + public enum GameStateChangeEvent { + NO_RESPAWN_BLOCK_AVAILABLE(0), + START_RAINING(1), + STOP_RAINING(2), + CHANGE_GAME_MODE(3), + WIN_GAME(4), + DEMO_EVENT(5), + ARROW_HIT_PLAYER(6), + RAIN_LEVEL_CHANGE(7), + THUNDER_LEVEL_CHANGE(8), + PUFFER_FISH_STING(9), + GUARDIAN_ELDER_EFFECT(10), + IMMEDIATE_RESPAWN(11), + LIMITED_CRAFTING(12), + LEVEL_CHUNKS_LOAD_START(13); + + private final int id; + + GameStateChangeEvent(int id) { + this.id = id; + } + + public int getId() { + return id; + } + } + + private GameStateChangeEvent event; private float value; - public PacketPlayOutGameEvent(byte event, float value) { + public PacketPlayOutGameStateChange(GameStateChangeEvent event, float value) { this.event = event; this.value = value; } - public int getEvent() { + public GameStateChangeEvent getEvent() { return event; } @@ -46,7 +73,7 @@ public class PacketPlayOutGameEvent extends PacketOut { DataOutputStream output = new DataOutputStream(buffer); output.writeByte(Packet.getPlayOut().get(getClass())); - output.writeByte(Byte.toUnsignedInt(event)); + output.writeByte(event.getId()); output.writeFloat(value); return buffer.toByteArray(); diff --git a/src/main/java/com/loohp/limbo/player/Player.java b/src/main/java/com/loohp/limbo/player/Player.java index ce2a1c8..804e33a 100644 --- a/src/main/java/com/loohp/limbo/player/Player.java +++ b/src/main/java/com/loohp/limbo/player/Player.java @@ -45,7 +45,7 @@ import com.loohp.limbo.network.protocol.packets.ClientboundSetTitlesAnimationPac import com.loohp.limbo.network.protocol.packets.ClientboundSystemChatPacket; import com.loohp.limbo.network.protocol.packets.PacketOut; import com.loohp.limbo.network.protocol.packets.PacketPlayOutCloseWindow; -import com.loohp.limbo.network.protocol.packets.PacketPlayOutGameState; +import com.loohp.limbo.network.protocol.packets.PacketPlayOutGameStateChange; import com.loohp.limbo.network.protocol.packets.PacketPlayOutHeldItemChange; import com.loohp.limbo.network.protocol.packets.PacketPlayOutNamedSoundEffect; import com.loohp.limbo.network.protocol.packets.PacketPlayOutOpenWindow; @@ -151,7 +151,7 @@ public class Player extends LivingEntity implements CommandSender, InventoryHold public void setGamemode(GameMode gamemode) { if (!this.gamemode.equals(gamemode)) { try { - PacketPlayOutGameState state = new PacketPlayOutGameState(3, gamemode.getId()); + PacketPlayOutGameStateChange state = new PacketPlayOutGameStateChange(PacketPlayOutGameStateChange.GameStateChangeEvent.CHANGE_GAME_MODE, gamemode.getId()); clientConnection.sendPacket(state); } catch (IOException e) { e.printStackTrace(); diff --git a/src/main/java/com/loohp/limbo/player/PlayerInteractManager.java b/src/main/java/com/loohp/limbo/player/PlayerInteractManager.java index 9497770..9c57313 100644 --- a/src/main/java/com/loohp/limbo/player/PlayerInteractManager.java +++ b/src/main/java/com/loohp/limbo/player/PlayerInteractManager.java @@ -30,7 +30,6 @@ import com.loohp.limbo.network.protocol.packets.PacketPlayOutEntityDestroy; import com.loohp.limbo.network.protocol.packets.PacketPlayOutEntityMetadata; import com.loohp.limbo.network.protocol.packets.PacketPlayOutSpawnEntity; import com.loohp.limbo.network.protocol.packets.PacketPlayOutUnloadChunk; -import com.loohp.limbo.utils.GameMode; import com.loohp.limbo.world.ChunkPosition; import com.loohp.limbo.world.World; import net.querz.mca.Chunk; @@ -47,131 +46,118 @@ import java.util.Set; import java.util.stream.Collectors; public class PlayerInteractManager { + + private Player player; + + private Set entities; + private Map currentViewing; + + public PlayerInteractManager() { + this.player = null; + this.entities = new HashSet<>(); + this.currentViewing = new HashMap<>(); + } + + protected void setPlayer(Player player) { + if (this.player == null) { + this.player = player; + } else { + throw new RuntimeException("Player in PlayerInteractManager cannot be changed once created"); + } + } + + public Player getPlayer() { + return player; + } - private Player player; + public void update() throws IOException { + if (player.clientConnection.getClientState() != ClientConnection.ClientState.PLAY) { + return; + } - private Set entities; - private Map currentViewing; - private final Map chunkUpdates; + int viewDistanceChunks = Limbo.getInstance().getServerProperties().getViewDistance(); + int viewDistanceBlocks = viewDistanceChunks << 4; + Location location = player.getLocation(); + Set entitiesInRange = player.getWorld().getEntities().stream().filter(each -> each.getLocation().distanceSquared(location) < viewDistanceBlocks * viewDistanceBlocks).collect(Collectors.toSet()); + for (Entity entity : entitiesInRange) { + if (!entities.contains(entity)) { + PacketPlayOutSpawnEntity packet = new PacketPlayOutSpawnEntity(entity.getEntityId(), entity.getUniqueId(), entity.getType(), entity.getX(), entity.getY(), entity.getZ(), entity.getYaw(), entity.getPitch(), entity.getPitch(), 0, (short) 0, (short) 0, (short) 0); + player.clientConnection.sendPacket(packet); - public PlayerInteractManager() { - this.player = null; - this.entities = new HashSet<>(); - this.currentViewing = new HashMap<>(); - this.chunkUpdates = new HashMap<>(); - } + PacketPlayOutEntityMetadata meta = new PacketPlayOutEntityMetadata(entity); + player.clientConnection.sendPacket(meta); + } + } + List ids = new ArrayList<>(); + for (Entity entity : entities) { + if (!entitiesInRange.contains(entity)) { + ids.add(entity.getEntityId()); + } + } + for (int id : ids) { + PacketPlayOutEntityDestroy packet = new PacketPlayOutEntityDestroy(id); + player.clientConnection.sendPacket(packet); + } - protected void setPlayer(Player player) { - if (this.player == null) { - this.player = player; - } else { - throw new RuntimeException("Player in PlayerInteractManager cannot be changed once created"); - } - } + entities = entitiesInRange; - public Player getPlayer() { - return player; - } + int playerChunkX = (int) location.getX() >> 4; + int playerChunkZ = (int) location.getZ() >> 4; + World world = location.getWorld(); - public void update() throws IOException { - if (player.clientConnection.getClientState() != ClientConnection.ClientState.PLAY && !player.clientConnection.isReady()) { - return; - } + Map chunksInRange = new HashMap<>(); - int viewDistanceChunks = Limbo.getInstance().getServerProperties().getViewDistance(); - int viewDistanceBlocks = viewDistanceChunks << 4; - Location location = player.getLocation(); - Set entitiesInRange = player.getWorld().getEntities().stream().filter(each -> each.getLocation().distanceSquared(location) < viewDistanceBlocks * viewDistanceBlocks).collect(Collectors.toSet()); - for (Entity entity : entitiesInRange) { - if (!entities.contains(entity)) { - PacketPlayOutSpawnEntity packet = new PacketPlayOutSpawnEntity(entity.getEntityId(), entity.getUniqueId(), entity.getType(), entity.getX(), entity.getY(), entity.getZ(), entity.getYaw(), entity.getPitch(), entity.getPitch(), 0, (short) 0, (short) 0, (short) 0); - player.clientConnection.sendPacket(packet); + for (int x = playerChunkX - viewDistanceChunks; x < playerChunkX + viewDistanceChunks; x++) { + for (int z = playerChunkZ - viewDistanceChunks; z < playerChunkZ + viewDistanceChunks; z++) { + Chunk chunk = world.getChunkAt(x, z); + if (chunk != null) { + chunksInRange.put(new ChunkPosition(world, x, z), chunk); + } else { + chunksInRange.put(new ChunkPosition(world, x, z), World.EMPTY_CHUNK); + } + } + } - PacketPlayOutEntityMetadata meta = new PacketPlayOutEntityMetadata(entity); - player.clientConnection.sendPacket(meta); - } - } - List ids = new ArrayList<>(); - for (Entity entity : entities) { - if (!entitiesInRange.contains(entity)) { - ids.add(entity.getEntityId()); - } - } - for (int id : ids) { - PacketPlayOutEntityDestroy packet = new PacketPlayOutEntityDestroy(id); - player.clientConnection.sendPacket(packet); - } + for (Entry entry : currentViewing.entrySet()) { + ChunkPosition chunkPos = entry.getKey(); + if (!chunksInRange.containsKey(chunkPos)) { + PacketPlayOutUnloadChunk packet = new PacketPlayOutUnloadChunk(chunkPos.getChunkX(), chunkPos.getChunkZ()); + player.clientConnection.sendPacket(packet); + } + } - entities = entitiesInRange; - - int playerChunkX = (int) location.getX() >> 4; - int playerChunkZ = (int) location.getZ() >> 4; - World world = location.getWorld(); - - Map chunksInRange = new HashMap<>(); - - for (int x = playerChunkX - viewDistanceChunks; x < playerChunkX + viewDistanceChunks; x++) { - for (int z = playerChunkZ - viewDistanceChunks; z < playerChunkZ + viewDistanceChunks; z++) { - Chunk chunk = world.getChunkAt(x, z); - if (chunk != null) { - chunksInRange.put(new ChunkPosition(world, x, z), chunk); + int counter = 0; + ClientboundChunkBatchStartPacket chunkBatchStartPacket = new ClientboundChunkBatchStartPacket(); + player.clientConnection.sendPacket(chunkBatchStartPacket); + for (Entry entry : chunksInRange.entrySet()) { + ChunkPosition chunkPos = entry.getKey(); + if (!currentViewing.containsKey(chunkPos)) { + Chunk chunk = chunkPos.getWorld().getChunkAt(chunkPos.getChunkX(), chunkPos.getChunkZ()); + if (chunk == null) { + ClientboundLevelChunkWithLightPacket chunkdata = new ClientboundLevelChunkWithLightPacket(chunkPos.getChunkX(), chunkPos.getChunkZ(), entry.getValue(), world.getEnvironment(), Collections.emptyList(), Collections.emptyList()); + player.clientConnection.sendPacket(chunkdata); } else { - chunksInRange.put(new ChunkPosition(world, x, z), World.EMPTY_CHUNK); + List blockChunk = world.getLightEngineBlock().getBlockLightBitMask(chunkPos.getChunkX(), chunkPos.getChunkZ()); + if (blockChunk == null) { + blockChunk = new ArrayList<>(); + } + List skyChunk = null; + if (world.hasSkyLight()) { + skyChunk = world.getLightEngineSky().getSkyLightBitMask(chunkPos.getChunkX(), chunkPos.getChunkZ()); + } + if (skyChunk == null) { + skyChunk = new ArrayList<>(); + } + ClientboundLevelChunkWithLightPacket chunkdata = new ClientboundLevelChunkWithLightPacket(chunkPos.getChunkX(), chunkPos.getChunkZ(), chunk, world.getEnvironment(), skyChunk, blockChunk); + player.clientConnection.sendPacket(chunkdata); } + counter++; } - } + } + ClientboundChunkBatchFinishedPacket chunkBatchFinishedPacket = new ClientboundChunkBatchFinishedPacket(counter); + player.clientConnection.sendPacket(chunkBatchFinishedPacket); - for (ChunkPosition chunkPos : currentViewing.keySet()) { - if (!chunksInRange.containsKey(chunkPos)) { - PacketPlayOutUnloadChunk packet = new PacketPlayOutUnloadChunk(chunkPos.getChunkX(), chunkPos.getChunkZ()); - player.clientConnection.sendPacket(packet); - } - } - - // add chunk candidates for updating - chunkUpdates.clear(); - chunkUpdates.putAll(chunksInRange); - - // blocks cannot be broken, so once we've sent all of them, don't update them anymore - if (getPlayer().getGamemode() == GameMode.ADVENTURE) { - for (ChunkPosition chunkPos : currentViewing.keySet()) { - chunkUpdates.remove(chunkPos); - } - } - - // if we don't have any chunk updates, don't send any packets - if (chunkUpdates.isEmpty()) return; - - int counter = 0; - ClientboundChunkBatchStartPacket chunkBatchStartPacket = new ClientboundChunkBatchStartPacket(); - player.clientConnection.sendPacket(chunkBatchStartPacket); - for (Entry entry : chunkUpdates.entrySet()) { - ChunkPosition chunkPos = entry.getKey(); - Chunk chunk = chunkPos.getWorld().getChunkAt(chunkPos.getChunkX(), chunkPos.getChunkZ()); - if (chunk == null) { - ClientboundLevelChunkWithLightPacket chunkdata = new ClientboundLevelChunkWithLightPacket(chunkPos.getChunkX(), chunkPos.getChunkZ(), entry.getValue(), world.getEnvironment(), Collections.emptyList(), Collections.emptyList()); - player.clientConnection.sendPacket(chunkdata); - } else { - List blockChunk = world.getLightEngineBlock().getBlockLightBitMask(chunkPos.getChunkX(), chunkPos.getChunkZ()); - if (blockChunk == null) { - blockChunk = new ArrayList<>(); - } - List skyChunk = null; - if (world.hasSkyLight()) { - skyChunk = world.getLightEngineSky().getSkyLightBitMask(chunkPos.getChunkX(), chunkPos.getChunkZ()); - } - if (skyChunk == null) { - skyChunk = new ArrayList<>(); - } - ClientboundLevelChunkWithLightPacket chunkdata = new ClientboundLevelChunkWithLightPacket(chunkPos.getChunkX(), chunkPos.getChunkZ(), chunk, world.getEnvironment(), skyChunk, blockChunk); - player.clientConnection.sendPacket(chunkdata); - } - counter++; - } - ClientboundChunkBatchFinishedPacket chunkBatchFinishedPacket = new ClientboundChunkBatchFinishedPacket(counter); - player.clientConnection.sendPacket(chunkBatchFinishedPacket); - - currentViewing = chunksInRange; - } + currentViewing = chunksInRange; + } } diff --git a/src/main/java/com/loohp/limbo/registry/Registry.java b/src/main/java/com/loohp/limbo/registry/Registry.java index a9086e5..72dab8a 100644 --- a/src/main/java/com/loohp/limbo/registry/Registry.java +++ b/src/main/java/com/loohp/limbo/registry/Registry.java @@ -48,20 +48,17 @@ public class Registry { static { String name = "registries.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(); - } - } Map blockEntityType = new HashMap<>(); Key defaultItemKey = null; BiMap itemIds = HashBiMap.create(); Map menuIds = new HashMap<>(); - try (InputStreamReader reader = new InputStreamReader(new FileInputStream(file), StandardCharsets.UTF_8)) { + + InputStream inputStream = Limbo.class.getClassLoader().getResourceAsStream(name); + if (inputStream == null) { + throw new RuntimeException("Failed to load " + name + " from jar!"); + } + try (InputStreamReader reader = new InputStreamReader(inputStream, StandardCharsets.UTF_8)) { JSONObject json = (JSONObject) new JSONParser().parse(reader); JSONObject blockEntityJson = (JSONObject) ((JSONObject) json.get("minecraft:block_entity_type")).get("entries"); diff --git a/src/main/java/com/loohp/limbo/world/DimensionRegistry.java b/src/main/java/com/loohp/limbo/world/DimensionRegistry.java index 323bf7e..c1480a6 100644 --- a/src/main/java/com/loohp/limbo/world/DimensionRegistry.java +++ b/src/main/java/com/loohp/limbo/world/DimensionRegistry.java @@ -37,24 +37,16 @@ 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 (InputStreamReader reader = new InputStreamReader(Files.newInputStream(reg.toPath()), StandardCharsets.UTF_8)) { + + InputStream inputStream = Limbo.class.getClassLoader().getResourceAsStream(name); + if (inputStream == null) { + throw new RuntimeException("Failed to load " + name + " from jar!"); + } + try (InputStreamReader reader = new InputStreamReader(inputStream, StandardCharsets.UTF_8)) { JSONObject json = (JSONObject) new JSONParser().parse(reader); CompoundTag tag = CustomNBTUtils.getCompoundTagFromJson((JSONObject) json.get("value")); defaultTag = tag; @@ -64,10 +56,6 @@ public class DimensionRegistry { } } - public File getFile() { - return reg; - } - public void resetCodec() { codec = defaultTag.clone(); } diff --git a/src/main/java/com/loohp/limbo/world/GeneratedBlockDataMappings.java b/src/main/java/com/loohp/limbo/world/GeneratedBlockDataMappings.java index 4375327..e9dd234 100644 --- a/src/main/java/com/loohp/limbo/world/GeneratedBlockDataMappings.java +++ b/src/main/java/com/loohp/limbo/world/GeneratedBlockDataMappings.java @@ -30,6 +30,8 @@ import java.io.File; import java.io.FileReader; import java.io.IOException; import java.io.InputStream; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.util.HashMap; import java.util.Map; @@ -41,17 +43,12 @@ public class GeneratedBlockDataMappings { 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)); + InputStream inputStream = Limbo.class.getClassLoader().getResourceAsStream(block); + if (inputStream == null) { + throw new RuntimeException("Failed to load " + block + " from jar!"); + } + try (InputStreamReader reader = new InputStreamReader(inputStream, StandardCharsets.UTF_8)) { + globalPalette = (JSONObject) new JSONParser().parse(reader); } catch (IOException | ParseException e) { e.printStackTrace(); } diff --git a/src/main/resources/mapping.json b/src/main/resources/mapping.json index a9c649a..365a148 100644 --- a/src/main/resources/mapping.json +++ b/src/main/resources/mapping.json @@ -43,12 +43,12 @@ "PacketPlayOutLogin": "0x29", "PacketPlayOutPositionAndLook": "0x3E", "PacketPlayOutSpawnPosition": "0x54", - "PacketPlayOutGameEvent": "0x20", "ClientboundSystemChatPacket": "0x69", "PacketPlayOutPlayerAbilities": "0x36", "ClientboundLevelChunkWithLightPacket": "0x25", "PacketPlayOutUnloadChunk": "0x1F", "PacketPlayOutKeepAlive": "0x15", + "PacketPlayOutGameStateChange": "0x20", "PacketPlayOutPlayerInfo": "0x3C", "PacketPlayOutUpdateViewPosition": "0x52", "PacketPlayOutDisconnect": "0x1B", @@ -56,7 +56,6 @@ "PacketPlayOutTabComplete": "0x10", "PacketPlayOutDeclareCommands": "0x11", "PacketPlayOutRespawn": "0x45", - "PacketPlayOutGameState": "0x20", "PacketPlayOutEntityDestroy": "0x40", "PacketPlayOutEntityMetadata": "0x56", "PacketPlayOutSpawnEntity": "0x01", @@ -87,4 +86,4 @@ "PacketStatusOutResponse": "0x00", "PacketStatusOutPong": "0x01" } -} +} \ No newline at end of file