force show loading screen to player via packet PacketPlayOutRespawn

Discussion in 'Spigot Plugin Development' started by i998979, Dec 6, 2019.

  1. Hi everyone, recently I'm trying to do something like this:
    Showing "Joining world" screen to the player without killing them (aka force show loading screen)
    And I've done something like this
    Code (Text):
        public static void setDimension(Player player) {
            boolean isFlying = player.isFlying();
            Location loc = player.getLocation();
           
            String difficulty = player.getWorld().getDifficulty().name();
            String gamemode = player.getGameMode().name();
           
            try {
                Class<?> dimensionManagerClass = ReflectionUtils.getNMSClass("DimensionManager");
                Object dimensionManager = dimensionManagerClass.getField("NETHER").get(dimensionManagerClass);
               
                Method enumDifficulty = ReflectionUtils.getNMSClass("EnumDifficulty").getMethod("valueOf", String.class);
                enumDifficulty.setAccessible(true);
                Object diff = enumDifficulty.invoke( ReflectionUtils.getNMSClass("EnumDifficulty"), difficulty);
               
                Class<?> worldTypeClass = ReflectionUtils.getNMSClass("WorldType");
                Object worldType = worldTypeClass.getField("NORMAL").get(worldTypeClass);
               
                Method method2 = ReflectionUtils.getNMSClass("EnumGamemode").getMethod("valueOf", String.class);
                method2.setAccessible(true);
                Object gm = method2.invoke(ReflectionUtils.getNMSClass("EnumGamemode"), gamemode);         
               
                Constructor<?> respawnConstructor = ReflectionUtils.getNMSClass("PacketPlayOutRespawn")
                        .getConstructor(ReflectionUtils.getNMSClass("DimensionManager"),
                                ReflectionUtils.getNMSClass("EnumDifficulty"),
                                ReflectionUtils.getNMSClass("WorldType"),
                                ReflectionUtils.getNMSClass("EnumGamemode"));
               
                Object packet = respawnConstructor.newInstance(dimensionManager, diff, worldType, gm);
                ReflectionUtils.sendPacket(player, packet);
               
                // Set back to OVERWORLD
                dimensionManagerClass = ReflectionUtils.getNMSClass("DimensionManager");
                dimensionManager = dimensionManagerClass.getField("OVERWORLD").get(dimensionManagerClass);
                packet = respawnConstructor.newInstance(dimensionManager, diff, worldType, gm);
                ReflectionUtils.sendPacket(player, packet);
           
            } catch (NoSuchMethodException e) {
                e.printStackTrace();
            } catch (SecurityException e) {
                e.printStackTrace();
            } catch (IllegalArgumentException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InstantiationException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            } catch (NoSuchFieldException e) {
                e.printStackTrace();
            }      
           
            Chunk chunk = player.getWorld().getChunkAt(player.getLocation());
            int distance = Bukkit.getServer().getViewDistance();
            for (int x = -distance; x < distance; x++) {
              for (int z = -distance; z < distance; z++) {
                  player.getWorld().refreshChunk(chunk.getX() + x, chunk.getZ() + z);
              }
            }
           
            //player.teleport(loc);
           
            Class<?> positionClass = ReflectionUtils.getNMSClass("PacketPlayOutPosition$EnumPlayerTeleportFlags");
           
            Set<Object> flags = new HashSet<>();
            flags.add(positionClass.getEnumConstants()[3]);
            flags.add(positionClass.getEnumConstants()[4]);
           
            //Set<PacketPlayOutPosition.EnumPlayerTeleportFlags> flags = Collections.unmodifiableSet(
                    //EnumSet.of(positionClass.getEnumConstants()[3],
                            //positionClass.getEnumConstants()[4]);
           
            try {
                Constructor<?> positionConstructor = ReflectionUtils.getNMSClass("PacketPlayOutPosition")
                        .getConstructor(double.class, double.class, double.class, float.class, float.class,
                                Set.class, int.class);
               
                Object packet = positionConstructor.newInstance(
                        player.getLocation().getX(), player.getLocation().getY(), player.getLocation().getZ(),
                            player.getLocation().getYaw() + 180, player.getLocation().getPitch(),
                            flags, 0);
               
                ReflectionUtils.sendPacket(player, packet);
               
            } catch (NoSuchMethodException e) {
                e.printStackTrace();
            } catch (SecurityException e) {
                e.printStackTrace();   
            } catch (InstantiationException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (IllegalArgumentException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            }
           
            if (isFlying) player.setFlying(true);
           
            player.updateInventory();
        }
    (I know the code is ugly and weird, but... yea)

    So what the code above does is
    - Send the first PacketPlayOutRespawn packet to the player and send him to NETHER
    - Then Send the same packet instantly but this time send them back to OVERWORLD
    vvvvvvvvv Problemssssss here
    - Refresh all visible chunks to the player

    Things are working great, but after refreshing the chunks to the player, the chunks outside viewDistance*viewDistance is invisible to the player, but the player is able to break those blocks as the block overlay still exists.
    Also, all entities in the manually loaded chunks are invisible to the player and they become visible again if the player moves away from them and comes back.

    So what I'm asking is how to make those chunks and entities visible to the player and avoid all potential errors/bugs.
    Or better way to achieve this and avoid all potential errors/bugs.

    Thank you soooo much <3