Last active
December 25, 2015 01:09
-
-
Save aadnk/6893017 to your computer and use it in GitHub Desktop.
Extend the time player's stay logged on on the server when they log out. This prevents PvP'ers from logging out in the middle of a fight.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.comphenix.example; | |
import java.io.ByteArrayOutputStream; | |
import java.io.DataOutputStream; | |
import java.io.IOException; | |
import java.io.OutputStream; | |
import java.net.InetSocketAddress; | |
import java.util.Set; | |
import java.util.concurrent.ConcurrentHashMap; | |
import java.util.concurrent.TimeUnit; | |
import org.bukkit.entity.Player; | |
import org.bukkit.event.Listener; | |
import org.bukkit.plugin.java.JavaPlugin; | |
import com.comphenix.protocol.Packets; | |
import com.comphenix.protocol.ProtocolLibrary; | |
import com.comphenix.protocol.ProtocolManager; | |
import com.comphenix.protocol.events.ConnectionSide; | |
import com.comphenix.protocol.events.PacketAdapter; | |
import com.comphenix.protocol.events.PacketEvent; | |
import com.comphenix.protocol.injector.GamePhase; | |
import com.comphenix.protocol.injector.ListenerInvoker; | |
import com.comphenix.protocol.injector.player.NetworkObjectInjector; | |
import com.comphenix.protocol.reflect.FieldUtils; | |
import com.google.common.collect.Sets; | |
import com.google.common.util.concurrent.Uninterruptibles; | |
public class ExtendLogoutTime extends JavaPlugin implements Listener { | |
private Set<InetSocketAddress> disconnected = Sets.newSetFromMap(new ConcurrentHashMap<InetSocketAddress, Boolean>()); | |
// The amount of seconds to delay logouts | |
private static final int DELAY_LOGOUT_SEC = 10; | |
private volatile boolean hasDetectedSpigot; | |
@Override | |
public void onEnable() { | |
final ProtocolManager manager = ProtocolLibrary.getProtocolManager(); | |
manager.addPacketListener( | |
new PacketAdapter(this, ConnectionSide.CLIENT_SIDE, GamePhase.LOGIN, Packets.Client.KICK_DISCONNECT, Packets.Client.LOGIN) { | |
@Override | |
public void onPacketReceiving(PacketEvent event) { | |
if (event.getPacket().getID() == Packets.Client.KICK_DISCONNECT) { | |
disconnected.add(event.getPlayer().getAddress()); | |
// Fall back on simply cancelling the packet | |
if (hasDetectedSpigot) { | |
event.setCancelled(true); | |
return; | |
} | |
// Don't flush the real stream | |
try { | |
handleOutputStream(manager, event.getPlayer()); | |
// Here's the trick - block this thread for 10 seconds | |
// If you need more than 60 seconds, you also need to handle | |
// the KEEP_ALIVE packets. | |
Uninterruptibles.sleepUninterruptibly(DELAY_LOGOUT_SEC, TimeUnit.SECONDS); | |
} catch (Exception e) { | |
getLogger().info("Detected Spigot"); | |
event.setCancelled(true); | |
hasDetectedSpigot = true; | |
} | |
} else { | |
disconnected.remove(event.getPlayer().getAddress()); | |
} | |
} | |
}); | |
// Stop writing packets to the client | |
manager.addPacketListener( | |
new PacketAdapter(this, ConnectionSide.SERVER_SIDE, GamePhase.LOGIN, Packets.Server.KEEP_ALIVE) { | |
@Override | |
public void onPacketSending(PacketEvent event) { | |
if (disconnected.contains(event.getPlayer().getAddress())) { | |
// Don't try to send any packets now ... | |
event.setCancelled(true); | |
} | |
} | |
}); | |
} | |
// Okay --- there's another trick. We need to replace the output stream with something harmless | |
private void handleOutputStream(ProtocolManager manager, Player player) { | |
try { | |
// | |
NetworkObjectInjector injector = new NetworkObjectInjector(getClassLoader(), | |
ProtocolLibrary.getErrorReporter(), null, (ListenerInvoker) manager, null); | |
injector.initializePlayer(player); | |
Object network = injector.getNetworkManager(); | |
OutputStream original = (OutputStream) FieldUtils.readField(network, "output", true); | |
// Just write a dummy output stream back | |
FieldUtils.writeField(network, "output", | |
new DataOutputStream(new ByteArrayOutputStream()), true); | |
// Clean up the network reference here | |
original.close(); | |
} catch (IllegalAccessException e) { | |
e.printStackTrace(); | |
} catch (IOException e) { | |
e.printStackTrace(); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment