+ * When this channel is used for writing an internal buffer grows to accommodate incoming data. The natural size limit is the value of {@link Integer#MAX_VALUE} + * and it is not possible to {@link #position(long) set the position} or {@link #truncate truncate} to a value bigger than that. Internal buffer can be accessed + * via {@link SeekableInMemoryByteChannel#array()}. + *
+ * + * @since 1.13 + * @NotThreadSafe + */ +public class SeekableInMemoryByteChannel implements SeekableByteChannel { + + private static final int NAIVE_RESIZE_LIMIT = Integer.MAX_VALUE >> 1; + + private byte[] data; + private final AtomicBoolean closed = new AtomicBoolean(); + private int position, size; + + /** + * Constructs a new instance from a byte array. + *+ * This constructor is intended to be used with pre-allocated buffer or when reading from a given byte array. + *
+ * + * @param data input data or pre-allocated array. + */ + public SeekableInMemoryByteChannel(final byte[] data) { + this.data = data; + this.size = data.length; + } + + /** + * Constructs a new instance from a size of storage to be allocated. + *+ * Creates a channel and allocates internal storage of a given size. + *
+ * + * @param size size of internal buffer to allocate, in bytes. + */ + public SeekableInMemoryByteChannel(final int size) { + this(new byte[size]); + } + + /** + * Obtains the array backing this channel. + *+ * NOTE: The returned buffer is not aligned with containing data, use {@link #size()} to obtain the size of data stored in the buffer. + *
+ * + * @return internal byte array. + */ + public byte[] array() { + return data; + } + + @Override + public void close() { + closed.set(true); + } + + private void ensureOpen() throws ClosedChannelException { + if (!isOpen()) { + throw new ClosedChannelException(); + } + } + + @Override + public boolean isOpen() { + return !closed.get(); + } + + /** + * Returns this channel's position. + *+ * This method violates the contract of {@link SeekableByteChannel#position()} as it will not throw any exception when invoked on a closed channel. Instead + * it will return the position the channel had when close has been called. + *
+ */ + @Override + public long position() { + return position; + } + + @Override + public SeekableByteChannel position(final long newPosition) throws IOException { + ensureOpen(); + if (newPosition < 0L || newPosition > Integer.MAX_VALUE) { + throw new IOException("Position has to be in range 0.. " + Integer.MAX_VALUE); + } + position = (int) newPosition; + return this; + } + + @Override + public int read(final ByteBuffer buf) throws IOException { + ensureOpen(); + int wanted = buf.remaining(); + final int possible = size - position; + if (possible <= 0) { + return -1; + } + if (wanted > possible) { + wanted = possible; + } + buf.put(data, position, wanted); + position += wanted; + return wanted; + } + + private void resize(final int newLength) { + int len = data.length; + if (len <= 0) { + len = 1; + } + if (newLength < NAIVE_RESIZE_LIMIT) { + while (len < newLength) { + len <<= 1; + } + } else { // avoid overflow + len = newLength; + } + data = Arrays.copyOf(data, len); + } + + /** + * Returns the current size of entity to which this channel is connected. + *+ * This method violates the contract of {@link SeekableByteChannel#size} as it will not throw any exception when invoked on a closed channel. Instead it + * will return the size the channel had when close has been called. + *
+ */ + @Override + public long size() { + return size; + } + + /** + * Truncates the entity, to which this channel is connected, to the given size. + *+ * This method violates the contract of {@link SeekableByteChannel#truncate} as it will not throw any exception when invoked on a closed channel. + *
+ * + * @throws IllegalArgumentException if size is negative or bigger than the maximum of a Java integer + */ + @Override + public SeekableByteChannel truncate(final long newSize) { + if (newSize < 0L || newSize > Integer.MAX_VALUE) { + throw new IllegalArgumentException("Size has to be in range 0.. " + Integer.MAX_VALUE); + } + if (size > newSize) { + size = (int) newSize; + } + if (position > newSize) { + position = (int) newSize; + } + return this; + } + + @Override + public int write(final ByteBuffer b) throws IOException { + ensureOpen(); + int wanted = b.remaining(); + final int possibleWithoutResize = size - position; + if (wanted > possibleWithoutResize) { + final int newSize = position + wanted; + if (newSize < 0) { // overflow + resize(Integer.MAX_VALUE); + wanted = Integer.MAX_VALUE - position; + } else { + resize(newSize); + } + } + b.get(data, position, wanted); + position += wanted; + if (size < position) { + size = position; + } + return wanted; + } + +} diff --git a/geyser/src/main/resources/META-INF/services/io.github.kosmx.emotes.server.services.InstanceService b/geyser/src/main/resources/META-INF/services/io.github.kosmx.emotes.server.services.InstanceService new file mode 100644 index 000000000..1102bbde7 --- /dev/null +++ b/geyser/src/main/resources/META-INF/services/io.github.kosmx.emotes.server.services.InstanceService @@ -0,0 +1 @@ +org.redlance.dima_dencep.mods.emotecraft.geyser.EmotecraftService \ No newline at end of file diff --git a/geyser/src/main/resources/extension.yml b/geyser/src/main/resources/extension.yml new file mode 100644 index 000000000..0a3fe8b3e --- /dev/null +++ b/geyser/src/main/resources/extension.yml @@ -0,0 +1,6 @@ +id: emotecraft +name: EmotecraftExt +api: 2.6.1 +main: org.redlance.dima_dencep.mods.emotecraft.geyser.EmotecraftExt +version: ${version} +authors: [dima_dencep, KosmX] diff --git a/gradle.properties b/gradle.properties index 369e8a6db..f84981c33 100644 --- a/gradle.properties +++ b/gradle.properties @@ -32,3 +32,4 @@ loom.ignoreDependencyLoomVersionValidation=true searchables_version = 1.0.2 fabric_permissions_api = 0.6.1 noteblocklib_version = 3.1.1 + geyser_version = 2.9.2-SNAPSHOT diff --git a/settings.gradle.kts b/settings.gradle.kts index 4869c8617..4c4f3021f 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -23,3 +23,6 @@ include("minecraft:neoforge") // Paper plugin include("paper") + +// Geyser ext +include("geyser")