11package net .potato .tuff ;
22
3- import org .bukkit .ChatColor ;
4- import org .bukkit .command .Command ;
5- import org .bukkit .command .CommandSender ;
6- import org .bukkit .command .TabCompleter ;
7- import org .bukkit .Chunk ;
8- import org .bukkit .GameMode ;
9- import org .bukkit .Location ;
10- import org .bukkit .Material ;
11- import org .bukkit .World ;
3+ import org .bukkit .*;
124import org .bukkit .block .Block ;
5+ import org .bukkit .command .*;
136import org .bukkit .entity .Player ;
14- import org .bukkit .event .EventHandler ;
15- import org .bukkit .event .EventPriority ;
16- import org .bukkit .event .Listener ;
17- import org .bukkit .event .block .Action ;
18- import org .bukkit .event .player .PlayerInteractEvent ;
19- import org .bukkit .event .player .PlayerJoinEvent ;
20- import org .bukkit .event .player .PlayerQuitEvent ;
21- import org .bukkit .event .player .PlayerTeleportEvent ;
22- import org .bukkit .event .player .PlayerTeleportEvent .TeleportCause ;
7+ import org .bukkit .event .*;
8+ import org .bukkit .event .block .*;
9+ import org .bukkit .event .player .*;
2310import org .bukkit .event .world .ChunkLoadEvent ;
2411import org .bukkit .inventory .ItemStack ;
2512import org .bukkit .plugin .java .JavaPlugin ;
2613import org .bukkit .plugin .messaging .PluginMessageListener ;
2714import org .bukkit .scheduler .BukkitRunnable ;
2815
29- import java .io .ByteArrayInputStream ;
30- import java .io .ByteArrayOutputStream ;
31- import java .io .DataInputStream ;
32- import java .io .DataOutputStream ;
33- import java .io .IOException ;
16+ import java .io .*;
3417import java .nio .charset .StandardCharsets ;
35- import java .util .ArrayList ;
36- import java .util .Collections ;
37- import java .util .List ;
38- import java .util .Set ;
39- import java .util .UUID ;
18+ import java .util .*;
4019import java .util .concurrent .ConcurrentHashMap ;
4120import java .util .stream .Collectors ;
4221
43- import org .bukkit .event .block .BlockBreakEvent ;
44- import org .bukkit .event .block .BlockPlaceEvent ;
45- import org .bukkit .event .block .BlockPhysicsEvent ;
46- import org .bukkit .event .EventPriority ;
47-
48- import org .bukkit .ChunkSnapshot ;
49- import org .bukkit .Chunk ;
50-
5122public class TuffX extends JavaPlugin implements Listener , PluginMessageListener , TabCompleter {
5223
5324 public static final String CHANNEL = "eagler:below_y0" ;
@@ -66,50 +37,39 @@ public void onEnable() {
6637 }
6738
6839 @ Override
69- public boolean onCommand (CommandSender sender , Command command , String label , String [] args ) {
70- if (!command .getName ().equalsIgnoreCase ("tuffx" )) {
71- return false ;
72- }
40+ public void onDisable () {
41+ getServer ().getMessenger ().unregisterOutgoingPluginChannel (this );
42+ getServer ().getMessenger ().unregisterIncomingPluginChannel (this );
43+ sentSections .clear ();
44+ if (!isMuted ) getLogger ().info ("TuffX disabled" );
45+ }
7346
47+ @ Override
48+ public boolean onCommand (CommandSender sender , Command command , String label , String [] args ) {
49+ if (!command .getName ().equalsIgnoreCase ("tuffx" )) return false ;
7450 if (args .length == 0 ) {
7551 handleHelpCommand (sender );
7652 return true ;
7753 }
78-
7954 switch (args [0 ].toLowerCase ()) {
80- case "mute" :
81- handleMuteCommand (sender );
82- break ;
83- case "reload" :
84- handleReloadCommand (sender );
85- break ;
86- case "reloadchunks" :
87- handleReloadChunksCommand (sender );
88- break ;
89- case "help" :
90- handleHelpCommand (sender );
91- break ;
92- default :
93- sender .sendMessage (ChatColor .GOLD + "[TuffX] " + ChatColor .RED + "Unknown subcommand. Use /tuffx help." );
94- break ;
55+ case "mute" -> handleMuteCommand (sender );
56+ case "reload" -> handleReloadCommand (sender );
57+ case "reloadchunks" -> handleReloadChunksCommand (sender );
58+ case "help" -> handleHelpCommand (sender );
59+ default -> sender .sendMessage (ChatColor .GOLD + "[TuffX] " + ChatColor .RED + "Unknown subcommand. Use /tuffx help." );
9560 }
9661 return true ;
9762 }
9863
9964 @ Override
10065 public List <String > onTabComplete (CommandSender sender , Command command , String alias , String [] args ) {
101- if (command .getName ().equalsIgnoreCase ("tuffx" )) {
102- if (args .length == 1 ) {
103- List <String > subcommands = new ArrayList <>();
104- if (sender .hasPermission ("tuffx.mute" )) subcommands .add ("mute" );
105- if (sender .hasPermission ("tuffx.reload" )) subcommands .add ("reload" );
106- if (sender .hasPermission ("tuffx.reloadchunks" )) subcommands .add ("reloadchunks" );
107- if (sender .hasPermission ("tuffx.help" )) subcommands .add ("help" );
108-
109- return subcommands .stream ()
110- .filter (s -> s .toLowerCase ().startsWith (args [0 ].toLowerCase ()))
111- .collect (Collectors .toList ());
112- }
66+ if (command .getName ().equalsIgnoreCase ("tuffx" ) && args .length == 1 ) {
67+ List <String > subcommands = new ArrayList <>();
68+ if (sender .hasPermission ("tuffx.mute" )) subcommands .add ("mute" );
69+ if (sender .hasPermission ("tuffx.reload" )) subcommands .add ("reload" );
70+ if (sender .hasPermission ("tuffx.reloadchunks" )) subcommands .add ("reloadchunks" );
71+ if (sender .hasPermission ("tuffx.help" )) subcommands .add ("help" );
72+ return subcommands .stream ().filter (s -> s .startsWith (args [0 ].toLowerCase ())).collect (Collectors .toList ());
11373 }
11474 return Collections .emptyList ();
11575 }
@@ -129,51 +89,108 @@ private void handleReloadCommand(CommandSender sender) {
12989 sender .sendMessage (ChatColor .RED + "You don't have permission to use this command." );
13090 return ;
13191 }
132- sender .sendMessage (ChatColor .GOLD + "[TuffX] " + ChatColor . YELLOW + " Reloading TuffX ..." );
92+ sender .sendMessage (ChatColor .GOLD + "[TuffX] Reloading..." );
13393 onDisable ();
13494 onEnable ();
135- sender .sendMessage (ChatColor .GOLD + "[TuffX] " + ChatColor . GREEN + " Reload complete." );
95+ sender .sendMessage (ChatColor .GOLD + "[TuffX] Reload complete." );
13696 }
13797
13898 private void handleReloadChunksCommand (CommandSender sender ) {
13999 if (!sender .hasPermission ("tuffx.reloadchunks" )) {
140100 sender .sendMessage (ChatColor .RED + "You don't have permission to use this command." );
141101 return ;
142102 }
143- sender .sendMessage (ChatColor .GOLD + "[TuffX] " + ChatColor . YELLOW + " Clearing chunk cache and resending to all players ..." );
103+ sender .sendMessage (ChatColor .GOLD + "[TuffX] Clearing chunk cache and resending..." );
144104 sentSections .clear ();
145-
146105 for (Player player : getServer ().getOnlinePlayers ()) {
147106 Chunk playerChunk = player .getLocation ().getChunk ();
148107 int viewDistance = getServer ().getViewDistance ();
149108 World world = player .getWorld ();
150-
151109 for (int x = -viewDistance ; x <= viewDistance ; x ++) {
152110 for (int z = -viewDistance ; z <= viewDistance ; z ++) {
153111 if (world .isChunkLoaded (playerChunk .getX () + x , playerChunk .getZ () + z )) {
154- Chunk chunk = world .getChunkAt (playerChunk .getX () + x , playerChunk .getZ () + z );
155- sendChunkSectionsAsync (player , chunk );
112+ sendChunkSectionsAsync (player , world .getChunkAt (playerChunk .getX () + x , playerChunk .getZ () + z ));
113+ }
114+ }
115+ }
116+ }
117+ sender .sendMessage (ChatColor .GOLD + "[TuffX] Chunk reload complete." );
118+ }
119+
120+ private byte [] createSectionPayload (World world , int cx , int cz , int sectionY ) throws IOException {
121+ Chunk chunk = world .getChunkAt (cx , cz );
122+ ChunkSnapshot snapshot = chunk .getChunkSnapshot (true , false , false );
123+ try (ByteArrayOutputStream bout = new ByteArrayOutputStream (8200 );
124+ DataOutputStream out = new DataOutputStream (bout )) {
125+ out .writeUTF ("chunk_data" );
126+ out .writeInt (cx );
127+ out .writeInt (cz );
128+ out .writeInt (sectionY );
129+ int baseY = sectionY * 16 ;
130+ for (int y = 0 ; y < 16 ; y ++) {
131+ int worldY = baseY + y ;
132+ for (int z = 0 ; z < 16 ; z ++) {
133+ for (int x = 0 ; x < 16 ; x ++) {
134+ Material type ;
135+ if (worldY >= world .getMinHeight () && worldY < world .getMaxHeight ()) {
136+ try {
137+ type = snapshot .getBlockType (x , worldY , z );
138+ } catch (Exception e ) {
139+ type = Material .AIR ;
140+ }
141+ } else {
142+ type = Material .AIR ;
143+ }
144+ out .writeShort (LegacyBlockIdManager .getLegacyShort (type ));
156145 }
157146 }
158147 }
148+ return bout .toByteArray ();
159149 }
160- sender .sendMessage (ChatColor .GOLD + "[TuffX] " + ChatColor .GREEN + "Chunk reload initiated for all online players. Reload is complete." );
161150 }
162151
152+ private void sendChunkSectionsAsync (Player player , Chunk chunk ) {
153+ new BukkitRunnable () {
154+ @ Override
155+ public void run () {
156+ int cx = chunk .getX ();
157+ int cz = chunk .getZ ();
158+ UUID uid = player .getUniqueId ();
159+ String worldName = chunk .getWorld ().getName ();
160+ World world = chunk .getWorld ();
161+
162+ int minSectionY = Math .max (world .getMinHeight () >> 4 , -4 );
163+ int maxSectionY = 0 ;
164+ for (int sectionY = minSectionY ; sectionY < maxSectionY ; sectionY ++) {
165+ ChunkSectionKey key = new ChunkSectionKey (uid , worldName , cx , cz , sectionY );
166+ if (sentSections .contains (key )) continue ;
167+ try {
168+ byte [] payload = createSectionPayload (world , cx , cz , sectionY );
169+ new BukkitRunnable () {
170+ @ Override
171+ public void run () {
172+ if (player .isOnline ()) {
173+ player .sendPluginMessage (TuffX .this , CHANNEL , payload );
174+ sentSections .add (key );
175+ }
176+ }
177+ }.runTask (TuffX .this );
178+ } catch (IOException e ) {
179+ if (!isMuted ) getLogger ().severe ("Failed to create section payload: " + key );
180+ }
181+ }
182+ }
183+ }.runTaskAsynchronously (this );
184+ }
185+
186+ private record ChunkSectionKey (UUID playerId , String worldName , int cx , int cz , int sectionY ) {}
187+
163188 private void handleHelpCommand (CommandSender sender ) {
164189 sender .sendMessage (ChatColor .GOLD + "--- TuffX Commands ---" );
165- if (sender .hasPermission ("tuffx.help" )) {
166- sender .sendMessage (ChatColor .YELLOW + "/tuffx help" + ChatColor .GRAY + " - Shows this help message." );
167- }
168- if (sender .hasPermission ("tuffx.mute" )) {
169- sender .sendMessage (ChatColor .YELLOW + "/tuffx mute" + ChatColor .GRAY + " - Toggles console logging for the plugin." );
170- }
171- if (sender .hasPermission ("tuffx.reload" )) {
172- sender .sendMessage (ChatColor .YELLOW + "/tuffx reload" + ChatColor .GRAY + " - Reloads the TuffX plugin." );
173- }
174- if (sender .hasPermission ("tuffx.reloadchunks" )) {
175- sender .sendMessage (ChatColor .YELLOW + "/tuffx reloadchunks" + ChatColor .GRAY + " - Resends below y0 chunks to players." );
176- }
190+ if (sender .hasPermission ("tuffx.help" )) sender .sendMessage (ChatColor .YELLOW + "/tuffx help" + ChatColor .GRAY + " - Shows help message" );
191+ if (sender .hasPermission ("tuffx.mute" )) sender .sendMessage (ChatColor .YELLOW + "/tuffx mute" + ChatColor .GRAY + " - Toggles plugin logging" );
192+ if (sender .hasPermission ("tuffx.reload" )) sender .sendMessage (ChatColor .YELLOW + "/tuffx reload" + ChatColor .GRAY + " - Reload plugin" );
193+ if (sender .hasPermission ("tuffx.reloadchunks" )) sender .sendMessage (ChatColor .YELLOW + "/tuffx reloadchunks" + ChatColor .GRAY + " - Resend chunks" );
177194 sender .sendMessage (ChatColor .GOLD + "----------------------" );
178195 }
179196
@@ -500,4 +517,4 @@ private void sendChunkSectionIfBelowY0(Player player, Block block) {
500517 }
501518 }
502519 }
503- }
520+ }
0 commit comments