mirror of
https://github.com/dmulloy2/ProtocolLib.git
synced 2024-11-30 22:53:26 +01:00
Attempt to handle snapshot versions by assuming Minecraft version.
The snapshot version contains a release date, so we'll simply compare that against a known release date of a Minecraft version. It it's later, we know it is at least a minor version above, and vice versa.
This commit is contained in:
parent
2001c15132
commit
1b1f36c5d6
@ -1,32 +1,32 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<classpath>
|
<classpath>
|
||||||
<classpathentry kind="src" output="target/classes" path="src/main/java">
|
<classpathentry kind="src" output="target/classes" path="src/main/java">
|
||||||
<attributes>
|
<attributes>
|
||||||
<attribute name="optional" value="true"/>
|
<attribute name="optional" value="true"/>
|
||||||
<attribute name="maven.pomderived" value="true"/>
|
<attribute name="maven.pomderived" value="true"/>
|
||||||
</attributes>
|
</attributes>
|
||||||
</classpathentry>
|
</classpathentry>
|
||||||
<classpathentry kind="src" output="target/test-classes" path="src/test/java">
|
<classpathentry kind="src" output="target/test-classes" path="src/test/java">
|
||||||
<attributes>
|
<attributes>
|
||||||
<attribute name="optional" value="true"/>
|
<attribute name="optional" value="true"/>
|
||||||
<attribute name="maven.pomderived" value="true"/>
|
<attribute name="maven.pomderived" value="true"/>
|
||||||
</attributes>
|
</attributes>
|
||||||
</classpathentry>
|
</classpathentry>
|
||||||
<classpathentry kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/4"/>
|
<classpathentry kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/4"/>
|
||||||
<classpathentry excluding="**" kind="src" output="target/classes" path="src/main/resources">
|
<classpathentry excluding="**" kind="src" output="target/classes" path="src/main/resources">
|
||||||
<attributes>
|
<attributes>
|
||||||
<attribute name="maven.pomderived" value="true"/>
|
<attribute name="maven.pomderived" value="true"/>
|
||||||
</attributes>
|
</attributes>
|
||||||
</classpathentry>
|
</classpathentry>
|
||||||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6">
|
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6">
|
||||||
<attributes>
|
<attributes>
|
||||||
<attribute name="maven.pomderived" value="true"/>
|
<attribute name="maven.pomderived" value="true"/>
|
||||||
</attributes>
|
</attributes>
|
||||||
</classpathentry>
|
</classpathentry>
|
||||||
<classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER">
|
<classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER">
|
||||||
<attributes>
|
<attributes>
|
||||||
<attribute name="maven.pomderived" value="true"/>
|
<attribute name="maven.pomderived" value="true"/>
|
||||||
</attributes>
|
</attributes>
|
||||||
</classpathentry>
|
</classpathentry>
|
||||||
<classpathentry kind="output" path="target/classes"/>
|
<classpathentry kind="output" path="target/classes"/>
|
||||||
</classpath>
|
</classpath>
|
||||||
|
@ -81,12 +81,17 @@ public class ProtocolLibrary extends JavaPlugin {
|
|||||||
/**
|
/**
|
||||||
* The minimum version ProtocolLib has been tested with.
|
* The minimum version ProtocolLib has been tested with.
|
||||||
*/
|
*/
|
||||||
private static final String MINIMUM_MINECRAFT_VERSION = "1.0.0";
|
public static final String MINIMUM_MINECRAFT_VERSION = "1.0.0";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The maximum version ProtocolLib has been tested with,
|
* The maximum version ProtocolLib has been tested with,
|
||||||
*/
|
*/
|
||||||
private static final String MAXIMUM_MINECRAFT_VERSION = "1.6.2";
|
public static final String MAXIMUM_MINECRAFT_VERSION = "1.6.2";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The date (with ISO 8601) when the most recent version was released.
|
||||||
|
*/
|
||||||
|
public static final String MINECRAFT_LAST_RELEASE_DATE = "2013-07-08";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The number of milliseconds per second.
|
* The number of milliseconds per second.
|
||||||
@ -376,7 +381,7 @@ public class ProtocolLibrary extends JavaPlugin {
|
|||||||
logger.warning("Version " + current + " has not yet been tested! Proceed with caution.");
|
logger.warning("Version " + current + " has not yet been tested! Proceed with caution.");
|
||||||
}
|
}
|
||||||
return current;
|
return current;
|
||||||
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
reporter.reportWarning(this, Report.newBuilder(REPORT_CANNOT_PARSE_MINECRAFT_VERSION).error(e));
|
reporter.reportWarning(this, Report.newBuilder(REPORT_CANNOT_PARSE_MINECRAFT_VERSION).error(e));
|
||||||
}
|
}
|
||||||
@ -384,7 +389,7 @@ public class ProtocolLibrary extends JavaPlugin {
|
|||||||
// Unknown version
|
// Unknown version
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void checkConflictingVersions() {
|
private void checkConflictingVersions() {
|
||||||
Pattern ourPlugin = Pattern.compile("ProtocolLib-(.*)\\.jar");
|
Pattern ourPlugin = Pattern.compile("ProtocolLib-(.*)\\.jar");
|
||||||
MinecraftVersion currentVersion = new MinecraftVersion(this.getDescription().getVersion());
|
MinecraftVersion currentVersion = new MinecraftVersion(this.getDescription().getVersion());
|
||||||
|
@ -1,305 +1,305 @@
|
|||||||
/*
|
/*
|
||||||
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
|
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
|
||||||
* Copyright (C) 2012 Kristian S. Stangeland
|
* Copyright (C) 2012 Kristian S. Stangeland
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify it under the terms of the
|
* This program is free software; you can redistribute it and/or modify it under the terms of the
|
||||||
* GNU General Public License as published by the Free Software Foundation; either version 2 of
|
* GNU General Public License as published by the Free Software Foundation; either version 2 of
|
||||||
* the License, or (at your option) any later version.
|
* the License, or (at your option) any later version.
|
||||||
*
|
*
|
||||||
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
* See the GNU General Public License for more details.
|
* See the GNU General Public License for more details.
|
||||||
*
|
*
|
||||||
* You should have received a copy of the GNU General Public License along with this program;
|
* You should have received a copy of the GNU General Public License along with this program;
|
||||||
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
||||||
* 02111-1307 USA
|
* 02111-1307 USA
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.comphenix.protocol.async;
|
package com.comphenix.protocol.async;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
import java.util.concurrent.PriorityBlockingQueue;
|
import java.util.concurrent.PriorityBlockingQueue;
|
||||||
|
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
|
|
||||||
import com.comphenix.protocol.events.PacketEvent;
|
import com.comphenix.protocol.events.PacketEvent;
|
||||||
import com.comphenix.protocol.injector.PlayerLoggedOutException;
|
import com.comphenix.protocol.injector.PlayerLoggedOutException;
|
||||||
import com.comphenix.protocol.reflect.FieldAccessException;
|
import com.comphenix.protocol.reflect.FieldAccessException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents packets ready to be transmitted to a client.
|
* Represents packets ready to be transmitted to a client.
|
||||||
* @author Kristian
|
* @author Kristian
|
||||||
*/
|
*/
|
||||||
abstract class PacketSendingQueue {
|
abstract class PacketSendingQueue {
|
||||||
|
|
||||||
public static final int INITIAL_CAPACITY = 10;
|
public static final int INITIAL_CAPACITY = 10;
|
||||||
|
|
||||||
private PriorityBlockingQueue<PacketEventHolder> sendingQueue;
|
private PriorityBlockingQueue<PacketEventHolder> sendingQueue;
|
||||||
|
|
||||||
// Asynchronous packet sending
|
// Asynchronous packet sending
|
||||||
private Executor asynchronousSender;
|
private Executor asynchronousSender;
|
||||||
|
|
||||||
// Whether or not packet transmission must occur on a specific thread
|
// Whether or not packet transmission must occur on a specific thread
|
||||||
private final boolean notThreadSafe;
|
private final boolean notThreadSafe;
|
||||||
|
|
||||||
// Whether or not we've run the cleanup procedure
|
// Whether or not we've run the cleanup procedure
|
||||||
private boolean cleanedUp = false;
|
private boolean cleanedUp = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a packet sending queue.
|
* Create a packet sending queue.
|
||||||
* @param notThreadSafe - whether or not to synchronize with the main thread or a background thread.
|
* @param notThreadSafe - whether or not to synchronize with the main thread or a background thread.
|
||||||
*/
|
*/
|
||||||
public PacketSendingQueue(boolean notThreadSafe, Executor asynchronousSender) {
|
public PacketSendingQueue(boolean notThreadSafe, Executor asynchronousSender) {
|
||||||
this.sendingQueue = new PriorityBlockingQueue<PacketEventHolder>(INITIAL_CAPACITY);
|
this.sendingQueue = new PriorityBlockingQueue<PacketEventHolder>(INITIAL_CAPACITY);
|
||||||
this.notThreadSafe = notThreadSafe;
|
this.notThreadSafe = notThreadSafe;
|
||||||
this.asynchronousSender = asynchronousSender;
|
this.asynchronousSender = asynchronousSender;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Number of packet events in the queue.
|
* Number of packet events in the queue.
|
||||||
* @return The number of packet events in the queue.
|
* @return The number of packet events in the queue.
|
||||||
*/
|
*/
|
||||||
public int size() {
|
public int size() {
|
||||||
return sendingQueue.size();
|
return sendingQueue.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Enqueue a packet for sending.
|
* Enqueue a packet for sending.
|
||||||
* @param packet - packet to queue.
|
* @param packet - packet to queue.
|
||||||
*/
|
*/
|
||||||
public void enqueue(PacketEvent packet) {
|
public void enqueue(PacketEvent packet) {
|
||||||
sendingQueue.add(new PacketEventHolder(packet));
|
sendingQueue.add(new PacketEventHolder(packet));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Invoked when one of the packets have finished processing.
|
* Invoked when one of the packets have finished processing.
|
||||||
* @param packetUpdated - the packet that has now been updated.
|
* @param packetUpdated - the packet that has now been updated.
|
||||||
* @param onMainThread - whether or not this is occuring on the main thread.
|
* @param onMainThread - whether or not this is occuring on the main thread.
|
||||||
*/
|
*/
|
||||||
public synchronized void signalPacketUpdate(PacketEvent packetUpdated, boolean onMainThread) {
|
public synchronized void signalPacketUpdate(PacketEvent packetUpdated, boolean onMainThread) {
|
||||||
|
|
||||||
AsyncMarker marker = packetUpdated.getAsyncMarker();
|
AsyncMarker marker = packetUpdated.getAsyncMarker();
|
||||||
|
|
||||||
// Should we reorder the event?
|
// Should we reorder the event?
|
||||||
if (marker.getQueuedSendingIndex() != marker.getNewSendingIndex() && !marker.hasExpired()) {
|
if (marker.getQueuedSendingIndex() != marker.getNewSendingIndex() && !marker.hasExpired()) {
|
||||||
PacketEvent copy = PacketEvent.fromSynchronous(packetUpdated, marker);
|
PacketEvent copy = PacketEvent.fromSynchronous(packetUpdated, marker);
|
||||||
|
|
||||||
// "Cancel" the original event
|
// "Cancel" the original event
|
||||||
packetUpdated.setReadOnly(false);
|
packetUpdated.setReadOnly(false);
|
||||||
packetUpdated.setCancelled(true);
|
packetUpdated.setCancelled(true);
|
||||||
|
|
||||||
// Enqueue the copy with the new sending index
|
// Enqueue the copy with the new sending index
|
||||||
enqueue(copy);
|
enqueue(copy);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mark this packet as finished
|
// Mark this packet as finished
|
||||||
marker.setProcessed(true);
|
marker.setProcessed(true);
|
||||||
trySendPackets(onMainThread);
|
trySendPackets(onMainThread);
|
||||||
}
|
}
|
||||||
|
|
||||||
/***
|
/***
|
||||||
* Invoked when a list of packet IDs are no longer associated with any listeners.
|
* Invoked when a list of packet IDs are no longer associated with any listeners.
|
||||||
* @param packetsRemoved - packets that no longer have any listeners.
|
* @param packetsRemoved - packets that no longer have any listeners.
|
||||||
* @param onMainThread - whether or not this is occuring on the main thread.
|
* @param onMainThread - whether or not this is occuring on the main thread.
|
||||||
*/
|
*/
|
||||||
public synchronized void signalPacketUpdate(List<Integer> packetsRemoved, boolean onMainThread) {
|
public synchronized void signalPacketUpdate(List<Integer> packetsRemoved, boolean onMainThread) {
|
||||||
|
|
||||||
Set<Integer> lookup = new HashSet<Integer>(packetsRemoved);
|
Set<Integer> lookup = new HashSet<Integer>(packetsRemoved);
|
||||||
|
|
||||||
// Note that this is O(n), so it might be expensive
|
// Note that this is O(n), so it might be expensive
|
||||||
for (PacketEventHolder holder : sendingQueue) {
|
for (PacketEventHolder holder : sendingQueue) {
|
||||||
PacketEvent event = holder.getEvent();
|
PacketEvent event = holder.getEvent();
|
||||||
|
|
||||||
if (lookup.contains(event.getPacketID())) {
|
if (lookup.contains(event.getPacketID())) {
|
||||||
event.getAsyncMarker().setProcessed(true);
|
event.getAsyncMarker().setProcessed(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is likely to have changed the situation a bit
|
// This is likely to have changed the situation a bit
|
||||||
trySendPackets(onMainThread);
|
trySendPackets(onMainThread);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Attempt to send any remaining packets.
|
* Attempt to send any remaining packets.
|
||||||
* @param onMainThread - whether or not this is occuring on the main thread.
|
* @param onMainThread - whether or not this is occuring on the main thread.
|
||||||
*/
|
*/
|
||||||
public void trySendPackets(boolean onMainThread) {
|
public void trySendPackets(boolean onMainThread) {
|
||||||
// Whether or not to continue sending packets
|
// Whether or not to continue sending packets
|
||||||
boolean sending = true;
|
boolean sending = true;
|
||||||
|
|
||||||
// Transmit as many packets as we can
|
// Transmit as many packets as we can
|
||||||
while (sending) {
|
while (sending) {
|
||||||
PacketEventHolder holder = sendingQueue.poll();
|
PacketEventHolder holder = sendingQueue.poll();
|
||||||
|
|
||||||
if (holder != null) {
|
if (holder != null) {
|
||||||
sending = processPacketHolder(onMainThread, holder);
|
sending = processPacketHolder(onMainThread, holder);
|
||||||
|
|
||||||
if (!sending) {
|
if (!sending) {
|
||||||
// Add it back again
|
// Add it back again
|
||||||
sendingQueue.add(holder);
|
sendingQueue.add(holder);
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// No more packets to send
|
// No more packets to send
|
||||||
sending = false;
|
sending = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Invoked when a packet might be ready for transmission.
|
* Invoked when a packet might be ready for transmission.
|
||||||
* @param onMainThread - TRUE if we're on the main thread, FALSE otherwise.
|
* @param onMainThread - TRUE if we're on the main thread, FALSE otherwise.
|
||||||
* @param holder - packet container.
|
* @param holder - packet container.
|
||||||
* @return TRUE to continue sending packets, FALSE otherwise.
|
* @return TRUE to continue sending packets, FALSE otherwise.
|
||||||
*/
|
*/
|
||||||
private boolean processPacketHolder(boolean onMainThread, final PacketEventHolder holder) {
|
private boolean processPacketHolder(boolean onMainThread, final PacketEventHolder holder) {
|
||||||
PacketEvent current = holder.getEvent();
|
PacketEvent current = holder.getEvent();
|
||||||
AsyncMarker marker = current.getAsyncMarker();
|
AsyncMarker marker = current.getAsyncMarker();
|
||||||
boolean hasExpired = marker.hasExpired();
|
boolean hasExpired = marker.hasExpired();
|
||||||
|
|
||||||
// Guard in cause the queue is closed
|
// Guard in cause the queue is closed
|
||||||
if (cleanedUp) {
|
if (cleanedUp) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// End condition?
|
// End condition?
|
||||||
if (marker.isProcessed() || hasExpired) {
|
if (marker.isProcessed() || hasExpired) {
|
||||||
if (hasExpired) {
|
if (hasExpired) {
|
||||||
// Notify timeout listeners
|
// Notify timeout listeners
|
||||||
onPacketTimeout(current);
|
onPacketTimeout(current);
|
||||||
|
|
||||||
// Recompute
|
// Recompute
|
||||||
marker = current.getAsyncMarker();
|
marker = current.getAsyncMarker();
|
||||||
hasExpired = marker.hasExpired();
|
hasExpired = marker.hasExpired();
|
||||||
|
|
||||||
// Could happen due to the timeout listeners
|
// Could happen due to the timeout listeners
|
||||||
if (!marker.isProcessed() && !hasExpired) {
|
if (!marker.isProcessed() && !hasExpired) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Is it okay to send the packet?
|
// Is it okay to send the packet?
|
||||||
if (!current.isCancelled() && !hasExpired) {
|
if (!current.isCancelled() && !hasExpired) {
|
||||||
// Make sure we're on the main thread
|
// Make sure we're on the main thread
|
||||||
if (notThreadSafe) {
|
if (notThreadSafe) {
|
||||||
try {
|
try {
|
||||||
boolean wantAsync = marker.isMinecraftAsync(current);
|
boolean wantAsync = marker.isMinecraftAsync(current);
|
||||||
boolean wantSync = !wantAsync;
|
boolean wantSync = !wantAsync;
|
||||||
|
|
||||||
// Wait for the next main thread heartbeat if we haven't fulfilled our promise
|
// Wait for the next main thread heartbeat if we haven't fulfilled our promise
|
||||||
if (!onMainThread && wantSync) {
|
if (!onMainThread && wantSync) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Let's give it what it wants
|
// Let's give it what it wants
|
||||||
if (onMainThread && wantAsync) {
|
if (onMainThread && wantAsync) {
|
||||||
asynchronousSender.execute(new Runnable() {
|
asynchronousSender.execute(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
// We know this isn't on the main thread
|
// We know this isn't on the main thread
|
||||||
processPacketHolder(false, holder);
|
processPacketHolder(false, holder);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Scheduler will do the rest
|
// Scheduler will do the rest
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (FieldAccessException e) {
|
} catch (FieldAccessException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
|
|
||||||
// Just drop the packet
|
// Just drop the packet
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Silently skip players that have logged out
|
// Silently skip players that have logged out
|
||||||
if (isOnline(current.getPlayer())) {
|
if (isOnline(current.getPlayer())) {
|
||||||
sendPacket(current);
|
sendPacket(current);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Drop the packet
|
// Drop the packet
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add it back and stop sending
|
// Add it back and stop sending
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Invoked when a packet has timed out.
|
* Invoked when a packet has timed out.
|
||||||
* @param event - the timed out packet.
|
* @param event - the timed out packet.
|
||||||
*/
|
*/
|
||||||
protected abstract void onPacketTimeout(PacketEvent event);
|
protected abstract void onPacketTimeout(PacketEvent event);
|
||||||
|
|
||||||
private boolean isOnline(Player player) {
|
private boolean isOnline(Player player) {
|
||||||
return player != null && player.isOnline();
|
return player != null && player.isOnline();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Send every packet, regardless of the processing state.
|
* Send every packet, regardless of the processing state.
|
||||||
*/
|
*/
|
||||||
private void forceSend() {
|
private void forceSend() {
|
||||||
while (true) {
|
while (true) {
|
||||||
PacketEventHolder holder = sendingQueue.poll();
|
PacketEventHolder holder = sendingQueue.poll();
|
||||||
|
|
||||||
if (holder != null) {
|
if (holder != null) {
|
||||||
sendPacket(holder.getEvent());
|
sendPacket(holder.getEvent());
|
||||||
} else {
|
} else {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether or not the packet transmission must synchronize with the main thread.
|
* Whether or not the packet transmission must synchronize with the main thread.
|
||||||
* @return TRUE if it must, FALSE otherwise.
|
* @return TRUE if it must, FALSE otherwise.
|
||||||
*/
|
*/
|
||||||
public boolean isSynchronizeMain() {
|
public boolean isSynchronizeMain() {
|
||||||
return notThreadSafe;
|
return notThreadSafe;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Transmit a packet, if it hasn't already.
|
* Transmit a packet, if it hasn't already.
|
||||||
* @param event - the packet to transmit.
|
* @param event - the packet to transmit.
|
||||||
*/
|
*/
|
||||||
private void sendPacket(PacketEvent event) {
|
private void sendPacket(PacketEvent event) {
|
||||||
|
|
||||||
AsyncMarker marker = event.getAsyncMarker();
|
AsyncMarker marker = event.getAsyncMarker();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Don't send a packet twice
|
// Don't send a packet twice
|
||||||
if (marker != null && !marker.isTransmitted()) {
|
if (marker != null && !marker.isTransmitted()) {
|
||||||
marker.sendPacket(event);
|
marker.sendPacket(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (PlayerLoggedOutException e) {
|
} catch (PlayerLoggedOutException e) {
|
||||||
System.out.println(String.format(
|
System.out.println(String.format(
|
||||||
"[ProtocolLib] Warning: Dropped packet index %s of ID %s",
|
"[ProtocolLib] Warning: Dropped packet index %s of ID %s",
|
||||||
marker.getOriginalSendingIndex(), event.getPacketID()
|
marker.getOriginalSendingIndex(), event.getPacketID()
|
||||||
));
|
));
|
||||||
|
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
// Just print the error
|
// Just print the error
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Automatically transmits every delayed packet.
|
* Automatically transmits every delayed packet.
|
||||||
*/
|
*/
|
||||||
public void cleanupAll() {
|
public void cleanupAll() {
|
||||||
if (!cleanedUp) {
|
if (!cleanedUp) {
|
||||||
// Note that the cleanup itself will always occur on the main thread
|
// Note that the cleanup itself will always occur on the main thread
|
||||||
forceSend();
|
forceSend();
|
||||||
|
|
||||||
// And we're done
|
// And we're done
|
||||||
cleanedUp = true;
|
cleanedUp = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,10 +17,13 @@
|
|||||||
|
|
||||||
package com.comphenix.protocol.utility;
|
package com.comphenix.protocol.utility;
|
||||||
|
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.util.Locale;
|
||||||
import java.util.regex.Matcher;
|
import java.util.regex.Matcher;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
import org.bukkit.Server;
|
import org.bukkit.Server;
|
||||||
|
import com.comphenix.protocol.ProtocolLibrary;
|
||||||
|
|
||||||
import com.google.common.base.Objects;
|
import com.google.common.base.Objects;
|
||||||
import com.google.common.collect.ComparisonChain;
|
import com.google.common.collect.ComparisonChain;
|
||||||
@ -35,15 +38,18 @@ public class MinecraftVersion implements Comparable<MinecraftVersion> {
|
|||||||
/**
|
/**
|
||||||
* Regular expression used to parse version strings.
|
* Regular expression used to parse version strings.
|
||||||
*/
|
*/
|
||||||
private static final String VERSION_PATTERN = ".*\\(.*MC.\\s*((?:\\d+\\.)*\\d)\\s*\\)";
|
private static final String VERSION_PATTERN = ".*\\(.*MC.\\s*([a-zA-z0-9\\-\\.]+)\\s*\\)";
|
||||||
|
|
||||||
private final int major;
|
private final int major;
|
||||||
private final int minor;
|
private final int minor;
|
||||||
private final int build;
|
private final int build;
|
||||||
|
|
||||||
// The development stage
|
// The development stage
|
||||||
private final String development;
|
private final String development;
|
||||||
|
|
||||||
|
// Snapshot?
|
||||||
|
private final SnapshotVersion snapshot;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determine the current Minecraft version.
|
* Determine the current Minecraft version.
|
||||||
* @param server - the Bukkit server that will be used to examine the MC version.
|
* @param server - the Bukkit server that will be used to examine the MC version.
|
||||||
@ -53,17 +59,53 @@ public class MinecraftVersion implements Comparable<MinecraftVersion> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Construct a version object from the format major.minor.build.
|
* Construct a version object from the format major.minor.build, or the snapshot format.
|
||||||
* @param versionOnly - the version in text form.
|
* @param versionOnly - the version in text form.
|
||||||
*/
|
*/
|
||||||
public MinecraftVersion(String versionOnly) {
|
public MinecraftVersion(String versionOnly) {
|
||||||
|
this(versionOnly, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct a version format from the standard release version or the snapshot verison.
|
||||||
|
* @param versionOnly - the version.
|
||||||
|
* @param parseSnapshot - TRUE to parse the snapshot, FALSE otherwise.
|
||||||
|
*/
|
||||||
|
private MinecraftVersion(String versionOnly, boolean parseSnapshot) {
|
||||||
String[] section = versionOnly.split("-");
|
String[] section = versionOnly.split("-");
|
||||||
int[] numbers = parseVersion(section[0]);
|
SnapshotVersion snapshot = null;
|
||||||
|
int[] numbers = new int[3];
|
||||||
|
|
||||||
|
try {
|
||||||
|
numbers = parseVersion(section[0]);
|
||||||
|
|
||||||
|
} catch (NumberFormatException cause) {
|
||||||
|
// Skip snapshot parsing
|
||||||
|
if (!parseSnapshot)
|
||||||
|
throw cause;
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Determine if the snapshot is newer than the current release version
|
||||||
|
snapshot = new SnapshotVersion(section[0]);
|
||||||
|
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd", Locale.US);
|
||||||
|
|
||||||
|
MinecraftVersion latest = new MinecraftVersion(ProtocolLibrary.MAXIMUM_MINECRAFT_VERSION, false);
|
||||||
|
boolean newer = snapshot.getSnapshotDate().compareTo(
|
||||||
|
format.parse(ProtocolLibrary.MINECRAFT_LAST_RELEASE_DATE)) > 0;
|
||||||
|
|
||||||
|
numbers[0] = latest.getMajor();
|
||||||
|
numbers[1] = latest.getMinor() + (newer ? 1 : -1);
|
||||||
|
numbers[2] = 0;
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new IllegalStateException("Cannot parse " + section[0], e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
this.major = numbers[0];
|
this.major = numbers[0];
|
||||||
this.minor = numbers[1];
|
this.minor = numbers[1];
|
||||||
this.build = numbers[2];
|
this.build = numbers[2];
|
||||||
this.development = section.length > 1 ? section[1] : null;
|
this.development = section.length > 1 ? section[1] : (snapshot != null ? "snapshot" : null);
|
||||||
|
this.snapshot = snapshot;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -88,6 +130,7 @@ public class MinecraftVersion implements Comparable<MinecraftVersion> {
|
|||||||
this.minor = minor;
|
this.minor = minor;
|
||||||
this.build = build;
|
this.build = build;
|
||||||
this.development = development;
|
this.development = development;
|
||||||
|
this.snapshot = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private int[] parseVersion(String version) {
|
private int[] parseVersion(String version) {
|
||||||
@ -136,6 +179,22 @@ public class MinecraftVersion implements Comparable<MinecraftVersion> {
|
|||||||
return development;
|
return development;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the snapshot version, or NULL if this is a release.
|
||||||
|
* @return The snapshot version.
|
||||||
|
*/
|
||||||
|
public SnapshotVersion getSnapshot() {
|
||||||
|
return snapshot;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine if this version is a snapshot.
|
||||||
|
* @return The snapshot version.
|
||||||
|
*/
|
||||||
|
public boolean isSnapshot() {
|
||||||
|
return snapshot != null;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve the version String (major.minor.build) only.
|
* Retrieve the version String (major.minor.build) only.
|
||||||
* @return A normal version string.
|
* @return A normal version string.
|
||||||
@ -144,7 +203,8 @@ public class MinecraftVersion implements Comparable<MinecraftVersion> {
|
|||||||
if (getDevelopmentStage() == null)
|
if (getDevelopmentStage() == null)
|
||||||
return String.format("%s.%s.%s", getMajor(), getMinor(), getBuild());
|
return String.format("%s.%s.%s", getMajor(), getMinor(), getBuild());
|
||||||
else
|
else
|
||||||
return String.format("%s.%s.%s-%s", getMajor(), getMinor(), getBuild(), getDevelopmentStage());
|
return String.format("%s.%s.%s-%s%s", getMajor(), getMinor(), getBuild(),
|
||||||
|
getDevelopmentStage(), isSnapshot() ? snapshot : "");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -158,6 +218,7 @@ public class MinecraftVersion implements Comparable<MinecraftVersion> {
|
|||||||
compare(getBuild(), o.getBuild()).
|
compare(getBuild(), o.getBuild()).
|
||||||
// No development String means it's a release
|
// No development String means it's a release
|
||||||
compare(getDevelopmentStage(), o.getDevelopmentStage(), Ordering.natural().nullsLast()).
|
compare(getDevelopmentStage(), o.getDevelopmentStage(), Ordering.natural().nullsLast()).
|
||||||
|
compare(getSnapshot(), o.getSnapshot()).
|
||||||
result();
|
result();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -207,4 +268,13 @@ public class MinecraftVersion implements Comparable<MinecraftVersion> {
|
|||||||
throw new IllegalStateException("Cannot parse version String '" + text + "'");
|
throw new IllegalStateException("Cannot parse version String '" + text + "'");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse the given server version into a Minecraft version.
|
||||||
|
* @param serverVersion - the server version.
|
||||||
|
* @return The resulting Minecraft version.
|
||||||
|
*/
|
||||||
|
public static MinecraftVersion fromServerVersion(String serverVersion) {
|
||||||
|
return new MinecraftVersion(extractVersion(serverVersion));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,108 @@
|
|||||||
|
package com.comphenix.protocol.utility;
|
||||||
|
|
||||||
|
import java.text.ParseException;
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
import com.google.common.base.Objects;
|
||||||
|
import com.google.common.collect.ComparisonChain;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used to parse a snapshot version.
|
||||||
|
* @author Kristian
|
||||||
|
*/
|
||||||
|
public class SnapshotVersion implements Comparable<SnapshotVersion> {
|
||||||
|
private static final Pattern SNAPSHOT_PATTERN = Pattern.compile("(\\d{2}w\\d{2})([a-z])");
|
||||||
|
|
||||||
|
private final String rawString;
|
||||||
|
private final Date snapshotDate;
|
||||||
|
private final int snapshotWeekVersion;
|
||||||
|
|
||||||
|
public SnapshotVersion(String version) {
|
||||||
|
Matcher matcher = SNAPSHOT_PATTERN.matcher(version.trim());
|
||||||
|
|
||||||
|
if (matcher.matches()) {
|
||||||
|
try {
|
||||||
|
this.snapshotDate = getDateFormat().parse(matcher.group(1));
|
||||||
|
this.snapshotWeekVersion = (int)matcher.group(2).charAt(0) - (int)'a';
|
||||||
|
this.rawString = version;
|
||||||
|
} catch (ParseException e) {
|
||||||
|
throw new IllegalArgumentException("Date implied by snapshot version is invalid.", e);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new IllegalArgumentException("Cannot parse " + version + " as a snapshot version.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the snapshot date parser.
|
||||||
|
* <p>
|
||||||
|
* We have to create a new instance of SimpleDateFormat every time as it is not thread safe.
|
||||||
|
* @return The date formatter.
|
||||||
|
*/
|
||||||
|
private static SimpleDateFormat getDateFormat() {
|
||||||
|
SimpleDateFormat format = new SimpleDateFormat("yy'w'ww", Locale.US);
|
||||||
|
format.setLenient(false);
|
||||||
|
return format;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the snapshot version within a week, starting at zero.
|
||||||
|
* @return The weekly version
|
||||||
|
*/
|
||||||
|
public int getSnapshotWeekVersion() {
|
||||||
|
return snapshotWeekVersion;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the week this snapshot was released.
|
||||||
|
* @return The week.
|
||||||
|
*/
|
||||||
|
public Date getSnapshotDate() {
|
||||||
|
return snapshotDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the raw snapshot string (yy'w'ww[a-z]).
|
||||||
|
* @return The snapshot string.
|
||||||
|
*/
|
||||||
|
public String getSnapshotString() {
|
||||||
|
return rawString;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compareTo(SnapshotVersion o) {
|
||||||
|
if (o == null)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
return ComparisonChain.start().
|
||||||
|
compare(snapshotDate, o.getSnapshotDate()).
|
||||||
|
compare(snapshotWeekVersion, o.getSnapshotWeekVersion()).
|
||||||
|
result();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (obj == this)
|
||||||
|
return true;
|
||||||
|
if (obj instanceof SnapshotVersion) {
|
||||||
|
SnapshotVersion other = (SnapshotVersion) obj;
|
||||||
|
return Objects.equal(snapshotDate, other.getSnapshotDate()) &&
|
||||||
|
snapshotWeekVersion == other.getSnapshotWeekVersion();
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hashCode(snapshotDate, snapshotWeekVersion);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return rawString;
|
||||||
|
}
|
||||||
|
}
|
@ -22,9 +22,9 @@ import static org.junit.Assert.*;
|
|||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import com.comphenix.protocol.utility.MinecraftVersion;
|
import com.comphenix.protocol.utility.MinecraftVersion;
|
||||||
|
import com.comphenix.protocol.utility.SnapshotVersion;
|
||||||
|
|
||||||
public class MinecraftVersionTest {
|
public class MinecraftVersionTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testComparision() {
|
public void testComparision() {
|
||||||
MinecraftVersion within = new MinecraftVersion(1, 2, 5);
|
MinecraftVersion within = new MinecraftVersion(1, 2, 5);
|
||||||
@ -38,6 +38,12 @@ public class MinecraftVersionTest {
|
|||||||
assertFalse(outside.compareTo(within) < 0 && outside.compareTo(highest) < 0);
|
assertFalse(outside.compareTo(within) < 0 && outside.compareTo(highest) < 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSnapshotVersion() {
|
||||||
|
MinecraftVersion version = MinecraftVersion.fromServerVersion("git-Spigot-1119 (MC: 13w39b)");
|
||||||
|
assertEquals(version.getSnapshot(), new SnapshotVersion("13w39b"));
|
||||||
|
}
|
||||||
|
|
||||||
public void testParsing() {
|
public void testParsing() {
|
||||||
assertEquals(MinecraftVersion.extractVersion("CraftBukkit R3.0 (MC: 1.4.3)"), "1.4.3");
|
assertEquals(MinecraftVersion.extractVersion("CraftBukkit R3.0 (MC: 1.4.3)"), "1.4.3");
|
||||||
assertEquals(MinecraftVersion.extractVersion("CraftBukkit Test Beta 1 (MC: 1.10.01 )"), "1.10.01");
|
assertEquals(MinecraftVersion.extractVersion("CraftBukkit Test Beta 1 (MC: 1.10.01 )"), "1.10.01");
|
||||||
|
@ -0,0 +1,41 @@
|
|||||||
|
package com.comphenix.protocol.utility;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
|
import java.util.Calendar;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
public class SnapshotVersionTest {
|
||||||
|
@Test
|
||||||
|
public void testDates() {
|
||||||
|
SnapshotVersion a = new SnapshotVersion("12w50b");
|
||||||
|
SnapshotVersion b = new SnapshotVersion("13w05a");
|
||||||
|
|
||||||
|
expect(a.getSnapshotDate(), 12, 50);
|
||||||
|
expect(b.getSnapshotDate(), 13, 5);
|
||||||
|
|
||||||
|
// Test equality
|
||||||
|
assertEquals(a, new SnapshotVersion("12w50b"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected=IllegalArgumentException.class)
|
||||||
|
public void testDateParsingProblem() {
|
||||||
|
// This date is not valid
|
||||||
|
new SnapshotVersion("12w80a");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected=IllegalArgumentException.class)
|
||||||
|
public void testMissingWeekVersion() {
|
||||||
|
new SnapshotVersion("13w05");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void expect(Date date, int year, int week) {
|
||||||
|
Calendar calendar = Calendar.getInstance(Locale.US);
|
||||||
|
calendar.setTime(date);
|
||||||
|
assertEquals(year, calendar.get(Calendar.YEAR) % 100);
|
||||||
|
assertEquals(week, calendar.get(Calendar.WEEK_OF_YEAR));
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user