Skip to content

Commit 9289825

Browse files
committed
Add packet logging for debugging
1 parent 57cfbc4 commit 9289825

File tree

5 files changed

+259
-6
lines changed

5 files changed

+259
-6
lines changed

modules/API/src/main/java/com/comphenix/protocol/PacketType.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -715,11 +715,11 @@ public static PacketType findCurrent(Protocol protocol, Sender sender, String na
715715
}
716716
}
717717

718-
private static String format(Protocol protocol, Sender sender, String name) {
718+
public static String format(Protocol protocol, Sender sender, String name) {
719719
if (name.contains("Packet"))
720720
return name;
721721

722-
return String.format("Packet%s%s%s", protocol.getPacketName(), sender.getPacketName(), name);
722+
return String.format("Packet%s%s%s", protocol.getPacketName(), sender.getPacketName(), WordUtils.capitalize(name));
723723
}
724724

725725
/**

modules/API/src/main/java/com/comphenix/protocol/injector/netty/WirePacket.java

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -149,9 +149,19 @@ private static byte[] getBytes(ByteBuf buffer) {
149149
* @return The resulting WirePacket
150150
*/
151151
public static WirePacket fromPacket(PacketContainer packet) {
152-
checkNotNull(packet, "packet cannot be null!");
153-
154152
int id = packet.getType().getCurrentId();
153+
return new WirePacket(id, getBytes(bufferFromPacket(packet)));
154+
}
155+
156+
/**
157+
* Creates a ByteBuf from an existing PacketContainer containing all the
158+
* bytes from that packet
159+
*
160+
* @param packet Existing packet
161+
* @return The ByteBuf
162+
*/
163+
public static ByteBuf bufferFromPacket(PacketContainer packet) {
164+
checkNotNull(packet, "packet cannot be null!");
155165

156166
ByteBuf buffer = PacketContainer.createPacketBuffer();
157167
Method write = MinecraftMethods.getPacketWriteByteBufMethod();
@@ -162,7 +172,7 @@ public static WirePacket fromPacket(PacketContainer packet) {
162172
throw new RuntimeException("Failed to serialize packet contents.", ex);
163173
}
164174

165-
return new WirePacket(id, getBytes(buffer));
175+
return buffer;
166176
}
167177

168178
/**
Lines changed: 232 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,232 @@
1+
/**
2+
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
3+
* Copyright (C) 2017 Dan Mulloy
4+
*
5+
* This program is free software; you can redistribute it and/or modify it under the terms of the
6+
* GNU General Public License as published by the Free Software Foundation; either version 2 of
7+
* the License, or (at your option) any later version.
8+
*
9+
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
10+
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11+
* See the GNU General Public License for more details.
12+
*
13+
* You should have received a copy of the GNU General Public License along with this program;
14+
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
15+
* 02111-1307 USA
16+
*/
17+
package com.comphenix.protocol;
18+
19+
import java.io.File;
20+
import java.io.IOException;
21+
import java.text.MessageFormat;
22+
import java.text.SimpleDateFormat;
23+
import java.util.ArrayList;
24+
import java.util.List;
25+
import java.util.logging.FileHandler;
26+
import java.util.logging.Formatter;
27+
import java.util.logging.Handler;
28+
import java.util.logging.Level;
29+
import java.util.logging.LogRecord;
30+
import java.util.logging.Logger;
31+
32+
import org.bukkit.ChatColor;
33+
import org.bukkit.command.Command;
34+
import org.bukkit.command.CommandExecutor;
35+
import org.bukkit.command.CommandSender;
36+
import org.bukkit.plugin.Plugin;
37+
38+
import com.comphenix.protocol.PacketType.Protocol;
39+
import com.comphenix.protocol.PacketType.Sender;
40+
import com.comphenix.protocol.events.ListeningWhitelist;
41+
import com.comphenix.protocol.events.PacketEvent;
42+
import com.comphenix.protocol.events.PacketListener;
43+
import com.comphenix.protocol.injector.netty.WirePacket;
44+
45+
import io.netty.buffer.ByteBuf;
46+
import io.netty.buffer.ByteBufUtil;
47+
48+
/**
49+
* Logs packets to a given stream
50+
* @author dmulloy2
51+
*/
52+
public class PacketLogging implements CommandExecutor, PacketListener {
53+
public static final String NAME = "packetlog";
54+
55+
private List<PacketType> sendingTypes = new ArrayList<>();
56+
private List<PacketType> receivingTypes = new ArrayList<>();
57+
58+
private ListeningWhitelist sendingWhitelist;
59+
private ListeningWhitelist receivingWhitelist;
60+
61+
private Logger fileLogger;
62+
private LogLocation location = LogLocation.FILE;
63+
64+
private final ProtocolManager manager;
65+
private final Plugin plugin;
66+
67+
PacketLogging(Plugin plugin, ProtocolManager manager) {
68+
this.plugin = plugin;
69+
this.manager = manager;
70+
}
71+
72+
@Override
73+
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
74+
PacketType type = null;
75+
76+
if (args.length > 2) {
77+
Protocol protocol;
78+
79+
try {
80+
protocol = Protocol.valueOf(args[0].toUpperCase());
81+
} catch (IllegalArgumentException ex) {
82+
sender.sendMessage(ChatColor.RED + "Unknown protocol " + args[0]);
83+
return true;
84+
}
85+
86+
Sender pSender;
87+
88+
try {
89+
pSender = Sender.valueOf(args[1].toUpperCase());
90+
} catch (IllegalArgumentException ex) {
91+
sender.sendMessage(ChatColor.RED + "Unknown sender: " + args[1]);
92+
return true;
93+
}
94+
95+
try {
96+
try {
97+
int id = Integer.parseInt(args[2]);
98+
type = PacketType.findCurrent(protocol, pSender, id);
99+
} catch (NumberFormatException ex) {
100+
type = PacketType.findCurrent(protocol, pSender, args[2]);
101+
}
102+
} catch (IllegalArgumentException ex) {
103+
sender.sendMessage(ChatColor.RED + "Unknown packet: " + PacketType.format(protocol, pSender, args[2]));
104+
return true;
105+
}
106+
107+
if (args.length > 3) {
108+
if (args[3].equalsIgnoreCase("console")) {
109+
this.location = LogLocation.CONSOLE;
110+
} else {
111+
this.location = LogLocation.FILE;
112+
}
113+
}
114+
115+
if (pSender == Sender.CLIENT) {
116+
if (receivingTypes.contains(type)) {
117+
receivingTypes.remove(type);
118+
} else {
119+
receivingTypes.add(type);
120+
}
121+
} else {
122+
if (sendingTypes.contains(type)) {
123+
sendingTypes.remove(type);
124+
} else {
125+
sendingTypes.add(type);
126+
}
127+
}
128+
129+
startLogging();
130+
sender.sendMessage(ChatColor.GREEN + "Now logging " + type.getPacketClass().getSimpleName());
131+
return true;
132+
}
133+
134+
sender.sendMessage(ChatColor.RED + "Invalid syntax: /packetlog <protocol> <sender> <packet> [location]");
135+
return true;
136+
}
137+
138+
private void startLogging() {
139+
manager.removePacketListener(this);
140+
141+
if (sendingTypes.isEmpty() && receivingTypes.isEmpty()) {
142+
return;
143+
}
144+
145+
this.sendingWhitelist = ListeningWhitelist.newBuilder().types(sendingTypes).build();
146+
this.receivingWhitelist = ListeningWhitelist.newBuilder().types(receivingTypes).build();
147+
148+
if (location == LogLocation.FILE && fileLogger == null) {
149+
fileLogger = Logger.getLogger("ProtocolLib-FileLogging");
150+
151+
for (Handler handler : fileLogger.getHandlers())
152+
fileLogger.removeHandler(handler);
153+
fileLogger.setUseParentHandlers(false);
154+
155+
try {
156+
File logFile = new File(plugin.getDataFolder(), "log.log");
157+
FileHandler handler = new FileHandler(logFile.getAbsolutePath(), true);
158+
handler.setFormatter(new LogFormatter());
159+
fileLogger.addHandler(handler);
160+
} catch (IOException ex) {
161+
plugin.getLogger().log(Level.SEVERE, "Failed to obtain log file:", ex);
162+
return;
163+
}
164+
}
165+
166+
manager.addPacketListener(this);
167+
}
168+
169+
@Override
170+
public void onPacketSending(PacketEvent event) {
171+
log(event);
172+
}
173+
174+
@Override
175+
public void onPacketReceiving(PacketEvent event) {
176+
log(event);
177+
}
178+
179+
private void log(PacketEvent event) {
180+
ByteBuf buffer = WirePacket.bufferFromPacket(event.getPacket());
181+
String hexDump = ByteBufUtil.hexDump(buffer);
182+
183+
if (location == LogLocation.FILE) {
184+
fileLogger.log(Level.INFO, event.getPacketType() + ":");
185+
fileLogger.log(Level.INFO, hexDump);
186+
fileLogger.log(Level.INFO, "");
187+
} else {
188+
System.out.println(event.getPacketType() + ":");
189+
System.out.println(hexDump);
190+
System.out.println();
191+
}
192+
}
193+
194+
@Override
195+
public ListeningWhitelist getSendingWhitelist() {
196+
return sendingWhitelist;
197+
}
198+
199+
@Override
200+
public ListeningWhitelist getReceivingWhitelist() {
201+
return receivingWhitelist;
202+
}
203+
204+
@Override
205+
public Plugin getPlugin() {
206+
return plugin;
207+
}
208+
209+
private static enum LogLocation {
210+
CONSOLE,
211+
FILE;
212+
}
213+
214+
private static class LogFormatter extends Formatter {
215+
private static final SimpleDateFormat DATE = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
216+
private static final String LINE_SEPARATOR = System.getProperty("line.separator");
217+
private static final String FORMAT = "[{0}] {1}";
218+
219+
@Override
220+
public String format(LogRecord record) {
221+
String string = formatMessage(record);
222+
if (string.isEmpty()) {
223+
return LINE_SEPARATOR;
224+
}
225+
226+
StringBuilder message = new StringBuilder();
227+
message.append(MessageFormat.format(FORMAT, DATE.format(record.getMillis()), string));
228+
message.append(LINE_SEPARATOR);
229+
return message.toString();
230+
}
231+
}
232+
}

modules/ProtocolLib/src/main/java/com/comphenix/protocol/ProtocolLib.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,8 @@ public class ProtocolLib extends JavaPlugin {
9090
private enum ProtocolCommand {
9191
FILTER,
9292
PACKET,
93-
PROTOCOL
93+
PROTOCOL,
94+
LOGGING;
9495
}
9596

9697
/**
@@ -143,6 +144,7 @@ private enum ProtocolCommand {
143144
private CommandProtocol commandProtocol;
144145
private CommandPacket commandPacket;
145146
private CommandFilter commandFilter;
147+
private PacketLogging packetLogging;
146148

147149
// Whether or not disable is not needed
148150
private boolean skipDisable;
@@ -269,6 +271,9 @@ private void initializeCommands() {
269271
case PACKET:
270272
commandPacket = new CommandPacket(reporter, this, logger, commandFilter, protocolManager);
271273
break;
274+
case LOGGING:
275+
packetLogging = new PacketLogging(this, protocolManager);
276+
break;
272277
}
273278
} catch (OutOfMemoryError e) {
274279
throw e;
@@ -404,6 +409,7 @@ public void onEnable() {
404409
registerCommand(CommandProtocol.NAME, commandProtocol);
405410
registerCommand(CommandPacket.NAME, commandPacket);
406411
registerCommand(CommandFilter.NAME, commandFilter);
412+
registerCommand(PacketLogging.NAME, packetLogging);
407413

408414
// Player login and logout events
409415
protocolManager.registerEvents(manager, this);

modules/ProtocolLib/src/main/resources/plugin.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,11 @@ commands:
2424
aliases: [packet_filter]
2525
permission: protocol.admin
2626
permission-message: You don't have <permission>
27+
packetlog:
28+
description: Logs hex representations of packets to a file or console
29+
usage: /<command> <protocol> <sender> <packet> [location]
30+
permission: protocol.admin
31+
permission-message: You don't have <permission>
2732

2833
permissions:
2934
protocol.*:

0 commit comments

Comments
 (0)