Attempting to add backwards compatibility for NetworkMarker.
Dieser Commit ist enthalten in:
Ursprung
92c8f21d5e
Commit
6159507bb4
@ -2,6 +2,7 @@ package com.comphenix.protocol.events;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
@ -10,6 +11,9 @@ import java.util.PriorityQueue;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
import com.comphenix.protocol.PacketType;
|
||||
import com.comphenix.protocol.utility.ByteBufferInputStream;
|
||||
import com.comphenix.protocol.utility.MinecraftReflection;
|
||||
import com.comphenix.protocol.utility.StreamSerializer;
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.primitives.Ints;
|
||||
@ -20,13 +24,35 @@ import com.google.common.primitives.Ints;
|
||||
*
|
||||
* @author Kristian
|
||||
*/
|
||||
public class NetworkMarker {
|
||||
public abstract class NetworkMarker {
|
||||
public static class EmptyBufferMarker extends NetworkMarker {
|
||||
public EmptyBufferMarker(@Nonnull ConnectionSide side) {
|
||||
super(side, (ByteBuffer)null, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected DataInputStream skipHeader(DataInputStream input) throws IOException {
|
||||
throw new IllegalStateException("Buffer is empty.");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ByteBuffer addHeader(ByteBuffer buffer, PacketType type) {
|
||||
throw new IllegalStateException("Buffer is empty.");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected DataInputStream addHeader(DataInputStream input, PacketType type) {
|
||||
throw new IllegalStateException("Buffer is empty.");
|
||||
}
|
||||
}
|
||||
|
||||
// Custom network handler
|
||||
private PriorityQueue<PacketOutputHandler> outputHandlers;
|
||||
|
||||
// The input buffer
|
||||
private ByteBuffer inputBuffer;
|
||||
|
||||
private final ConnectionSide side;
|
||||
private final PacketType type;
|
||||
|
||||
// Cache serializer too
|
||||
private StreamSerializer serializer;
|
||||
@ -36,9 +62,10 @@ public class NetworkMarker {
|
||||
* @param side - whether or not this marker belongs to a client or server packet.
|
||||
* @param inputBuffer - the read serialized packet data.
|
||||
*/
|
||||
public NetworkMarker(@Nonnull ConnectionSide side, ByteBuffer inputBuffer) {
|
||||
public NetworkMarker(@Nonnull ConnectionSide side, ByteBuffer inputBuffer, PacketType type) {
|
||||
this.side = Preconditions.checkNotNull(side, "side cannot be NULL.");
|
||||
this.inputBuffer = Preconditions.checkNotNull(inputBuffer, "inputBuffer cannot be NULL.");
|
||||
this.type = Preconditions.checkNotNull(type, "type cannot be NULL.");
|
||||
}
|
||||
|
||||
/**
|
||||
@ -47,10 +74,12 @@ public class NetworkMarker {
|
||||
* The input buffer is only non-null for client-side packets.
|
||||
* @param side - whether or not this marker belongs to a client or server packet.
|
||||
* @param inputBuffer - the read serialized packet data.
|
||||
* @param handler - handle skipping headers.
|
||||
*/
|
||||
public NetworkMarker(@Nonnull ConnectionSide side, byte[] inputBuffer) {
|
||||
public NetworkMarker(@Nonnull ConnectionSide side, byte[] inputBuffer, PacketType type) {
|
||||
this.side = Preconditions.checkNotNull(side, "side cannot be NULL.");
|
||||
|
||||
this.type = Preconditions.checkNotNull(type, "type cannot be NULL.");
|
||||
|
||||
if (inputBuffer != null) {
|
||||
this.inputBuffer = ByteBuffer.wrap(inputBuffer);
|
||||
}
|
||||
@ -75,7 +104,7 @@ public class NetworkMarker {
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the serialized packet data (excluding the header) from the network input stream.
|
||||
* Retrieve the serialized packet data (excluding the header by default) from the network input stream.
|
||||
* <p>
|
||||
* The returned buffer is read-only. If the parent event is a server side packet this
|
||||
* method throws {@link IllegalStateException}.
|
||||
@ -84,9 +113,49 @@ public class NetworkMarker {
|
||||
* @return A byte buffer containing the raw packet data read from the network.
|
||||
*/
|
||||
public ByteBuffer getInputBuffer() {
|
||||
return getInputBuffer(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the serialized packet data from the network input stream.
|
||||
* <p>
|
||||
* The returned buffer is read-only. If the parent event is a server side packet this
|
||||
* method throws {@link IllegalStateException}.
|
||||
* <p>
|
||||
* It returns NULL if the packet was transmitted by a plugin locally.
|
||||
* @param excludeHeader - whether or not to exclude the packet ID header.
|
||||
* @return A byte buffer containing the raw packet data read from the network.
|
||||
*/
|
||||
public ByteBuffer getInputBuffer(boolean excludeHeader) {
|
||||
if (side.isForServer())
|
||||
throw new IllegalStateException("Server-side packets have no input buffer.");
|
||||
return inputBuffer != null ? inputBuffer.asReadOnlyBuffer() : null;
|
||||
|
||||
if (inputBuffer != null) {
|
||||
ByteBuffer result = inputBuffer.asReadOnlyBuffer();
|
||||
|
||||
try {
|
||||
if (excludeHeader)
|
||||
result = skipHeader(result);
|
||||
else
|
||||
result = addHeader(result, type);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Cannot skip packet header.", e);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the serialized packet data (excluding the header by default) as an input stream.
|
||||
* <p>
|
||||
* The data is exactly the same as in {@link #getInputBuffer()}.
|
||||
* @see #getInputBuffer()
|
||||
* @param excludeHeader - whether or not to exclude the packet ID header.
|
||||
* @return The incoming serialized packet data as a stream, or NULL if the packet was transmitted locally.
|
||||
*/
|
||||
public DataInputStream getInputStream() {
|
||||
return getInputStream(true);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -94,17 +163,37 @@ public class NetworkMarker {
|
||||
* <p>
|
||||
* The data is exactly the same as in {@link #getInputBuffer()}.
|
||||
* @see #getInputBuffer()
|
||||
* @param excludeHeader - whether or not to exclude the packet ID header.
|
||||
* @return The incoming serialized packet data as a stream, or NULL if the packet was transmitted locally.
|
||||
*/
|
||||
public DataInputStream getInputStream() {
|
||||
@SuppressWarnings("resource")
|
||||
public DataInputStream getInputStream(boolean excludeHeader) {
|
||||
if (side.isForServer())
|
||||
throw new IllegalStateException("Server-side packets have no input buffer.");
|
||||
if (inputBuffer == null)
|
||||
return null;
|
||||
|
||||
return new DataInputStream(
|
||||
DataInputStream input = new DataInputStream(
|
||||
new ByteArrayInputStream(inputBuffer.array())
|
||||
);
|
||||
|
||||
try {
|
||||
if (excludeHeader)
|
||||
input = skipHeader(input);
|
||||
else
|
||||
input = addHeader(input, type);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Cannot skip packet header.", e);
|
||||
}
|
||||
return input;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether or not the output handlers have to write a packet header.
|
||||
* @return TRUE if they do, FALSE otherwise.
|
||||
*/
|
||||
public boolean requireOutputHeader() {
|
||||
return MinecraftReflection.isUsingNetty();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -172,6 +261,40 @@ public class NetworkMarker {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a byte buffer without the header in the current packet.
|
||||
* <p>
|
||||
* It's safe to modify the position of the buffer.
|
||||
* @param buffer - a read-only byte source.
|
||||
*/
|
||||
protected ByteBuffer skipHeader(ByteBuffer buffer) throws IOException {
|
||||
skipHeader(new DataInputStream(new ByteBufferInputStream(buffer)));
|
||||
return buffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an input stream without the header in the current packet.
|
||||
* <p>
|
||||
* It's safe to modify the input stream.
|
||||
*/
|
||||
protected abstract DataInputStream skipHeader(DataInputStream input) throws IOException;
|
||||
|
||||
/**
|
||||
* Return the byte buffer prepended with the packet header.
|
||||
* @param buffer - the read-only byte buffer.
|
||||
* @param type - the current packet.
|
||||
* @return The byte buffer.
|
||||
*/
|
||||
protected abstract ByteBuffer addHeader(ByteBuffer buffer, PacketType type);
|
||||
|
||||
/**
|
||||
* Return the input stream prepended with the packet header.
|
||||
* @param input - the input stream.
|
||||
* @param type - the current packet.
|
||||
* @return The byte buffer.
|
||||
*/
|
||||
protected abstract DataInputStream addHeader(DataInputStream input, PacketType type);
|
||||
|
||||
/**
|
||||
* Determine if the given marker has any output handlers.
|
||||
* @param marker - the marker to check.
|
||||
|
@ -207,8 +207,8 @@ public class PacketEvent extends EventObject implements Cancellable {
|
||||
public NetworkMarker getNetworkMarker() {
|
||||
if (networkMarker == null) {
|
||||
if (isServerPacket()) {
|
||||
networkMarker = new NetworkMarker(
|
||||
serverPacket ? ConnectionSide.SERVER_SIDE : ConnectionSide.CLIENT_SIDE, (byte[]) null);
|
||||
networkMarker = new NetworkMarker.EmptyBufferMarker(
|
||||
serverPacket ? ConnectionSide.SERVER_SIDE : ConnectionSide.CLIENT_SIDE);
|
||||
} else {
|
||||
throw new IllegalStateException("Add the option ListenerOptions.INTERCEPT_INPUT_BUFFER to your listener.");
|
||||
}
|
||||
|
@ -25,8 +25,10 @@ public interface PacketOutputHandler {
|
||||
/**
|
||||
* Invoked when a given packet is to be written to the output stream.
|
||||
* <p>
|
||||
* Note that the buffer is initially filled with the output from the default write method. This excludes
|
||||
* the packet ID header.
|
||||
* Note that the buffer is initially filled with the output from the default write method.
|
||||
* <p>
|
||||
* In Minecraft 1.6.4, the header is always excluded, whereas it MUST be included in Minecraft 1.7.2. Call
|
||||
* {@link NetworkMarker#requireOutputHeader()} to determine this.
|
||||
* @param event - the packet that will be outputted.
|
||||
* @param buffer - the data that is currently scheduled to be outputted.
|
||||
* @return The modified byte array to write. NULL is not permitted.
|
||||
|
@ -244,10 +244,6 @@ class ChannelInjector extends ByteToMessageDecoder {
|
||||
protected void encode(ChannelHandlerContext ctx, Object packet, ByteBuf output) throws Exception {
|
||||
ChannelInjector.this.encode(ctx, packet, output);
|
||||
}
|
||||
|
||||
public void exceptionCaught(ChannelHandlerContext channelhandlercontext, Throwable throwable) {
|
||||
throwable.printStackTrace();
|
||||
}
|
||||
};
|
||||
|
||||
// Insert our handlers - note that we effectively replace the vanilla encoder/decoder
|
||||
@ -369,7 +365,7 @@ class ChannelInjector extends ByteToMessageDecoder {
|
||||
|
||||
if (channelListener.includeBuffer(packetClass)) {
|
||||
byteBuffer.resetReaderIndex();
|
||||
marker = new NetworkMarker(ConnectionSide.CLIENT_SIDE, getBytes(byteBuffer));
|
||||
marker = new NettyNetworkMarker(ConnectionSide.CLIENT_SIDE, getBytes(byteBuffer));
|
||||
}
|
||||
Object output = channelListener.onPacketReceiving(this, input, marker);
|
||||
|
||||
|
@ -0,0 +1,40 @@
|
||||
package com.comphenix.protocol.injector.netty;
|
||||
|
||||
import java.io.DataInputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
import com.comphenix.protocol.PacketType;
|
||||
import com.comphenix.protocol.events.ConnectionSide;
|
||||
import com.comphenix.protocol.events.NetworkMarker;
|
||||
|
||||
class NettyNetworkMarker extends NetworkMarker {
|
||||
public NettyNetworkMarker(@Nonnull ConnectionSide side, byte[] inputBuffer) {
|
||||
super(side, inputBuffer, null);
|
||||
}
|
||||
|
||||
public NettyNetworkMarker(@Nonnull ConnectionSide side, ByteBuffer inputBuffer) {
|
||||
super(side, inputBuffer, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected DataInputStream skipHeader(DataInputStream input) throws IOException {
|
||||
// Skip the variable int containing the packet ID
|
||||
getSerializer().deserializeVarInt(input);
|
||||
return input;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ByteBuffer addHeader(ByteBuffer buffer, PacketType type) {
|
||||
// We don't have to add anything - it's already there
|
||||
return buffer;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected DataInputStream addHeader(DataInputStream input, PacketType type) {
|
||||
// As above
|
||||
return input;
|
||||
}
|
||||
}
|
@ -307,7 +307,7 @@ public class NettyProtocolInjector implements ChannelListener {
|
||||
return new AbstractPacketInjector(reveivedFilters) {
|
||||
@Override
|
||||
public PacketEvent packetRecieved(PacketContainer packet, Player client, byte[] buffered) {
|
||||
NetworkMarker marker = buffered != null ? new NetworkMarker(ConnectionSide.CLIENT_SIDE, buffered) : null;
|
||||
NetworkMarker marker = buffered != null ? new NettyNetworkMarker(ConnectionSide.CLIENT_SIDE, buffered) : null;
|
||||
ChannelInjector.fromPlayer(client, NettyProtocolInjector.this).
|
||||
saveMarker(packet.getHandle(), marker);
|
||||
return packetReceived(packet, client, marker);
|
||||
|
@ -0,0 +1,66 @@
|
||||
package com.comphenix.protocol.injector.packet;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
import com.comphenix.protocol.PacketType;
|
||||
import com.comphenix.protocol.events.ConnectionSide;
|
||||
import com.comphenix.protocol.events.NetworkMarker;
|
||||
import com.google.common.io.ByteStreams;
|
||||
import com.google.common.io.InputSupplier;
|
||||
import com.google.common.primitives.Bytes;
|
||||
|
||||
/**
|
||||
* Represents a network marker for 1.6.4 and earlier.
|
||||
* @author Kristian
|
||||
*/
|
||||
public class LegacyNetworkMarker extends NetworkMarker {
|
||||
public LegacyNetworkMarker(@Nonnull ConnectionSide side, byte[] inputBuffer, PacketType type) {
|
||||
super(side, inputBuffer, type);
|
||||
}
|
||||
|
||||
public LegacyNetworkMarker(@Nonnull ConnectionSide side, ByteBuffer inputBuffer, PacketType type) {
|
||||
super(side, inputBuffer, type);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected DataInputStream skipHeader(DataInputStream input) throws IOException {
|
||||
// This has already been done
|
||||
return input;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ByteBuffer addHeader(ByteBuffer buffer, PacketType type) {
|
||||
return ByteBuffer.wrap(Bytes.concat(new byte[] { (byte) type.getLegacyId() }, buffer.array()));
|
||||
}
|
||||
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
@Override
|
||||
protected DataInputStream addHeader(final DataInputStream input, final PacketType type) {
|
||||
InputSupplier<InputStream> header = new InputSupplier<InputStream>() {
|
||||
@Override
|
||||
public InputStream getInput() throws IOException {
|
||||
byte[] data = new byte[] { (byte) type.getLegacyId() };
|
||||
return new ByteArrayInputStream(data);
|
||||
}
|
||||
};
|
||||
|
||||
InputSupplier<InputStream> data = new InputSupplier<InputStream>() {
|
||||
@Override
|
||||
public InputStream getInput() throws IOException {
|
||||
return input;
|
||||
}
|
||||
};
|
||||
|
||||
// Combine them into a single stream
|
||||
try {
|
||||
return new DataInputStream(ByteStreams.join((InputSupplier) header, (InputSupplier) data).getInput());
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Cannot add header.", e);
|
||||
}
|
||||
}
|
||||
}
|
@ -349,7 +349,7 @@ class ProxyPacketInjector implements PacketInjector {
|
||||
|
||||
@Override
|
||||
public PacketEvent packetRecieved(PacketContainer packet, Player client, byte[] buffered) {
|
||||
NetworkMarker marker = buffered != null ? new NetworkMarker(ConnectionSide.CLIENT_SIDE, buffered) : null;
|
||||
NetworkMarker marker = buffered != null ? new LegacyNetworkMarker(ConnectionSide.CLIENT_SIDE, buffered, packet.getType()) : null;
|
||||
PacketEvent event = PacketEvent.fromClient((Object) manager, packet, marker, client);
|
||||
|
||||
manager.invokePacketRecieving(event);
|
||||
|
@ -35,6 +35,7 @@ import com.comphenix.protocol.events.PacketContainer;
|
||||
import com.comphenix.protocol.events.PacketEvent;
|
||||
import com.comphenix.protocol.injector.ListenerInvoker;
|
||||
import com.comphenix.protocol.injector.PlayerLoggedOutException;
|
||||
import com.comphenix.protocol.injector.packet.LegacyNetworkMarker;
|
||||
import com.comphenix.protocol.injector.packet.PacketInjector;
|
||||
import com.comphenix.protocol.injector.player.NetworkObjectInjector;
|
||||
import com.comphenix.protocol.injector.player.PlayerInjectionHandler;
|
||||
@ -486,7 +487,7 @@ public class SpigotPacketInjector implements SpigotPacketListener {
|
||||
* @return The packet event that was used.
|
||||
*/
|
||||
PacketEvent packetReceived(PacketContainer packet, Player sender, byte[] buffered) {
|
||||
NetworkMarker marker = buffered != null ? new NetworkMarker(ConnectionSide.CLIENT_SIDE, buffered) : null;
|
||||
NetworkMarker marker = buffered != null ? new LegacyNetworkMarker(ConnectionSide.CLIENT_SIDE, buffered, packet.getType()) : null;
|
||||
PacketEvent event = PacketEvent.fromClient(this, packet, marker, sender);
|
||||
|
||||
invoker.invokePacketRecieving(event);
|
||||
|
@ -0,0 +1,35 @@
|
||||
package com.comphenix.protocol.utility;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
/**
|
||||
* Represents an input stream that delegates to a byte buffer.
|
||||
* @author Kristian
|
||||
*/
|
||||
public class ByteBufferInputStream extends InputStream {
|
||||
private ByteBuffer buf;
|
||||
|
||||
public ByteBufferInputStream(ByteBuffer buf) {
|
||||
this.buf = buf;
|
||||
}
|
||||
|
||||
public int read() throws IOException {
|
||||
if (!buf.hasRemaining()) {
|
||||
return -1;
|
||||
}
|
||||
return buf.get() & 0xFF;
|
||||
}
|
||||
|
||||
public int read(byte[] bytes, int off, int len)
|
||||
throws IOException {
|
||||
if (!buf.hasRemaining()) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
len = Math.min(len, buf.remaining());
|
||||
buf.get(bytes, off, len);
|
||||
return len;
|
||||
}
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
package com.comphenix.protocol.utility;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
/**
|
||||
* Represents an output stream that is backed by a ByteBuffer.
|
||||
* @author Kristian
|
||||
*/
|
||||
public class ByteBufferOutputStream extends OutputStream {
|
||||
ByteBuffer buf;
|
||||
|
||||
public ByteBufferOutputStream(ByteBuffer buf) {
|
||||
this.buf = buf;
|
||||
}
|
||||
|
||||
public void write(int b) throws IOException {
|
||||
buf.put((byte) b);
|
||||
}
|
||||
|
||||
public void write(byte[] bytes, int off, int len)
|
||||
throws IOException {
|
||||
buf.put(bytes, off, len);
|
||||
}
|
||||
}
|
@ -17,6 +17,7 @@ import com.comphenix.protocol.reflect.FuzzyReflection.MethodAccessor;
|
||||
import com.comphenix.protocol.reflect.fuzzy.FuzzyMethodContract;
|
||||
import com.comphenix.protocol.wrappers.nbt.NbtCompound;
|
||||
import com.comphenix.protocol.wrappers.nbt.NbtFactory;
|
||||
import com.google.common.base.Preconditions;
|
||||
|
||||
/**
|
||||
* Utility methods for reading and writing Minecraft objects to streams.
|
||||
@ -24,6 +25,8 @@ import com.comphenix.protocol.wrappers.nbt.NbtFactory;
|
||||
* @author Kristian
|
||||
*/
|
||||
public class StreamSerializer {
|
||||
private static final StreamSerializer DEFAULT = new StreamSerializer();
|
||||
|
||||
// Cached methods
|
||||
private static MethodAccessor READ_ITEM_METHOD;
|
||||
private static MethodAccessor WRITE_ITEM_METHOD;
|
||||
@ -34,6 +37,52 @@ public class StreamSerializer {
|
||||
private static MethodAccessor READ_STRING_METHOD;
|
||||
private static MethodAccessor WRITE_STRING_METHOD;
|
||||
|
||||
/**
|
||||
* Retrieve a default stream serializer.
|
||||
* @return A serializer.
|
||||
*/
|
||||
public static StreamSerializer getDefault() {
|
||||
return DEFAULT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read a variable integer from an input stream.
|
||||
* @param source - the source.
|
||||
* @return The integer.
|
||||
* @throws IOException The source stream threw an exception.
|
||||
*/
|
||||
public int deserializeVarInt(@Nonnull DataInputStream source) throws IOException {
|
||||
Preconditions.checkNotNull(source, "source cannot be NULL");
|
||||
|
||||
int result = 0;
|
||||
int length = 0;
|
||||
byte currentByte;
|
||||
do {
|
||||
currentByte = source.readByte();
|
||||
result |= (currentByte & 0x7F) << length++ * 7;
|
||||
if (length > 5)
|
||||
throw new RuntimeException("VarInt too big");
|
||||
} while ((currentByte & 0x80) == 0x80);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write a variable integer to an output stream.
|
||||
* @param destination - the destination.
|
||||
* @param value - the value to write.
|
||||
* @throws IOException The destination stream threw an exception.
|
||||
*/
|
||||
public void serializeVarInt(@Nonnull DataOutputStream destination, int value) throws IOException {
|
||||
Preconditions.checkNotNull(destination, "source cannot be NULL");
|
||||
|
||||
while ((value & 0xFFFFFF80) != 0) {
|
||||
destination.writeByte(value & 0x7F | 0x80);
|
||||
value >>>= 7;
|
||||
}
|
||||
destination.writeByte(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read or deserialize an item stack from an underlying input stream.
|
||||
* <p>
|
||||
|
In neuem Issue referenzieren
Einen Benutzer sperren