Compare commits
140 Commits
8b195d1d21
...
master
Author | SHA1 | Date | |
---|---|---|---|
![]() |
7c7cb3de0f | ||
![]() |
88436c44a6 | ||
![]() |
bdd32d5a58 | ||
![]() |
5e59b6dc85 | ||
![]() |
bccce74c3c | ||
![]() |
0e9e0b58d2 | ||
![]() |
8cb49bc10a | ||
![]() |
5b05934fe8 | ||
![]() |
97f65726d2 | ||
![]() |
e3ab8ef15f | ||
![]() |
d5bcabdc60 | ||
![]() |
3cd530f007 | ||
![]() |
2b9808cd13 | ||
![]() |
70fa02f3a4 | ||
![]() |
4ebc3c96b2 | ||
![]() |
dd1531e28d | ||
![]() |
5709a65785 | ||
![]() |
5348aad094 | ||
![]() |
b60c1bdb37 | ||
![]() |
93508d5083 | ||
![]() |
1f159f8eaa | ||
![]() |
31f7ef8c54 | ||
![]() |
244412405a | ||
![]() |
9cd0d3289f | ||
![]() |
7cde213e63 | ||
![]() |
aa44ebe770 | ||
![]() |
5dad41034b | ||
![]() |
cd1ceb4c31 | ||
![]() |
23ba5141f1 | ||
![]() |
415ac8c81e | ||
![]() |
41e49dad6b | ||
![]() |
bec329352d | ||
![]() |
89e66ed648 | ||
![]() |
d8f9d81b30 | ||
![]() |
363003d8c7 | ||
![]() |
a696bb0e9f | ||
![]() |
68f4f6bd40 | ||
![]() |
442ff808f3 | ||
![]() |
fbbcc454d5 | ||
![]() |
f8de305477 | ||
![]() |
f1f5be18f9 | ||
![]() |
e05560976b | ||
![]() |
8bff00f15b | ||
![]() |
4d37c2488e | ||
![]() |
75456b2c0a | ||
![]() |
53365e4b18 | ||
![]() |
69e4872f40 | ||
![]() |
a336efb8fa | ||
![]() |
ae2fc30b7b | ||
![]() |
2bafb70581 | ||
![]() |
617c2728a2 | ||
![]() |
89069a362d | ||
![]() |
1279cca971 | ||
![]() |
26433bf021 | ||
![]() |
d81040cd6f | ||
![]() |
d7538df91b | ||
![]() |
2516de6586 | ||
![]() |
1da3a8c240 | ||
![]() |
f6151dce56 | ||
![]() |
9667743735 | ||
![]() |
7587f03306 | ||
![]() |
77b81f2612 | ||
![]() |
fa6d47732d | ||
![]() |
252e7b0027 | ||
![]() |
c820b3a062 | ||
![]() |
687c302610 | ||
![]() |
4b0262312e | ||
![]() |
6f13c2d6b6 | ||
![]() |
cd186999e5 | ||
![]() |
e3c7fd8cc5 | ||
![]() |
9476ffccdb | ||
![]() |
47f8c29a7c | ||
![]() |
458246505f | ||
![]() |
362bd0f4c4 | ||
![]() |
598d73e6f0 | ||
![]() |
f797bd488f | ||
![]() |
0d153feee7 | ||
![]() |
2a78233cc2 | ||
![]() |
591e18753d | ||
![]() |
556a15a6f8 | ||
![]() |
774a6fd68c | ||
![]() |
0070421549 | ||
![]() |
4dad940a2f | ||
![]() |
ed4a80eb0b | ||
![]() |
dd2033bf1a | ||
![]() |
cceebdad2a | ||
![]() |
05bdf5d3c1 | ||
![]() |
c3e4a6ef5b | ||
![]() |
2337acfcc1 | ||
![]() |
69861e5334 | ||
![]() |
22aa6f5faf | ||
![]() |
508c2f7ac3 | ||
![]() |
9dd5fb626d | ||
![]() |
b5ae0196fc | ||
![]() |
80bb237289 | ||
![]() |
4fded9828f | ||
![]() |
6b22690971 | ||
![]() |
60a3bf082f | ||
![]() |
0aa2871b26 | ||
![]() |
1265a9927b | ||
![]() |
d99570214a | ||
![]() |
15bd33b25b | ||
![]() |
7340f1a035 | ||
![]() |
8a80435e64 | ||
![]() |
20a71b06a9 | ||
![]() |
b376f61578 | ||
![]() |
373dab05ad | ||
![]() |
f6b40b1186 | ||
![]() |
81b118a8ba | ||
![]() |
7a42f12716 | ||
![]() |
4886c4be01 | ||
![]() |
7338d0f444 | ||
![]() |
8212e10c7c | ||
![]() |
2593130b3e | ||
![]() |
6ea49962c5 | ||
![]() |
672db9fe47 | ||
![]() |
2bacf6572b | ||
![]() |
9813e46e66 | ||
![]() |
01a5f36012 | ||
![]() |
f0a30c43cd | ||
![]() |
acb85e30fa | ||
![]() |
9437cedc48 | ||
![]() |
a89cf5f36d | ||
![]() |
b309e4ac50 | ||
![]() |
477ea5983c | ||
![]() |
eca6090f1e | ||
![]() |
8f8c270f3b | ||
![]() |
84ac7ab944 | ||
![]() |
5fbcc6b119 | ||
![]() |
79f85a2ce2 | ||
![]() |
d32eedd333 | ||
![]() |
e1d4b6adc7 | ||
![]() |
534148763f | ||
![]() |
cd56fb32c2 | ||
![]() |
6b612302e1 | ||
![]() |
e49759025f | ||
![]() |
c310e3339f | ||
![]() |
b64615e298 | ||
![]() |
45d2f44003 | ||
![]() |
a57adcce00 |
4
.github/workflows/maven.yml
vendored
4
.github/workflows/maven.yml
vendored
@@ -4,12 +4,12 @@ on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-22.04
|
||||
runs-on: ubuntu-24.04
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
java: [8, 11, 17, 21]
|
||||
java: [8, 11, 17, 21, 25-ea]
|
||||
|
||||
name: Java ${{ matrix.java }}
|
||||
|
||||
|
@@ -23,4 +23,4 @@ Binaries
|
||||
--------
|
||||
Precompiled binaries are available for end users on [Jenkins](https://www.spigotmc.org/go/bungeecord-dl).
|
||||
|
||||
(c) 2012-2024 SpigotMC Pty. Ltd.
|
||||
(c) 2012-2025 SpigotMC Pty. Ltd.
|
||||
|
10
api/pom.xml
10
api/pom.xml
@@ -6,13 +6,13 @@
|
||||
<parent>
|
||||
<groupId>net.md-5</groupId>
|
||||
<artifactId>bungeecord-parent</artifactId>
|
||||
<version>1.21-R0.1-SNAPSHOT</version>
|
||||
<version>1.21-R0.4-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
<groupId>net.md-5</groupId>
|
||||
<artifactId>bungeecord-api</artifactId>
|
||||
<version>1.21-R0.1-SNAPSHOT</version>
|
||||
<version>1.21-R0.4-SNAPSHOT</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>BungeeCord-API</name>
|
||||
@@ -31,6 +31,12 @@
|
||||
<version>${project.version}</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.md-5</groupId>
|
||||
<artifactId>bungeecord-dialog</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.md-5</groupId>
|
||||
<artifactId>bungeecord-event</artifactId>
|
||||
|
@@ -16,6 +16,7 @@ import net.md_5.bungee.api.connection.ProxiedPlayer;
|
||||
import net.md_5.bungee.api.plugin.Plugin;
|
||||
import net.md_5.bungee.api.plugin.PluginManager;
|
||||
import net.md_5.bungee.api.scheduler.TaskScheduler;
|
||||
import net.md_5.bungee.protocol.channel.BungeeChannelInitializer;
|
||||
|
||||
public abstract class ProxyServer
|
||||
{
|
||||
@@ -311,4 +312,56 @@ public abstract class ProxyServer
|
||||
*/
|
||||
public abstract Title createTitle();
|
||||
|
||||
/**
|
||||
* Get the unsafe methods of this class.
|
||||
*
|
||||
* @return the unsafe method interface
|
||||
*/
|
||||
public abstract Unsafe unsafe();
|
||||
|
||||
public interface Unsafe
|
||||
{
|
||||
|
||||
/**
|
||||
* Gets the frontend channel initializer
|
||||
*
|
||||
* @return the frontend channel initializer
|
||||
*/
|
||||
BungeeChannelInitializer getFrontendChannelInitializer();
|
||||
|
||||
/**
|
||||
* Set the frontend channel initializer of this proxy
|
||||
*
|
||||
* @param channelInitializer the frontend channelInitializer to set
|
||||
*/
|
||||
void setFrontendChannelInitializer(BungeeChannelInitializer channelInitializer);
|
||||
|
||||
/**
|
||||
* Gets the backend channel initializer
|
||||
*
|
||||
* @return the backend channel initializer
|
||||
*/
|
||||
BungeeChannelInitializer getBackendChannelInitializer();
|
||||
|
||||
/**
|
||||
* Set the backend channel initializer of this proxy
|
||||
*
|
||||
* @param channelInitializer the backend channelInitializer to set
|
||||
*/
|
||||
void setBackendChannelInitializer(BungeeChannelInitializer channelInitializer);
|
||||
|
||||
/**
|
||||
* Gets the server info channel initializer
|
||||
*
|
||||
* @return the server info channel initializer
|
||||
*/
|
||||
BungeeChannelInitializer getServerInfoChannelInitializer();
|
||||
|
||||
/**
|
||||
* Set the server info channel initializer of this proxy
|
||||
*
|
||||
* @param channelInitializer the server info channelInitializer to set
|
||||
*/
|
||||
void setServerInfoChannelInitializer(BungeeChannelInitializer channelInitializer);
|
||||
}
|
||||
}
|
||||
|
@@ -84,5 +84,15 @@ public interface Connection
|
||||
* @param packet the packet to send
|
||||
*/
|
||||
void sendPacket(DefinedPacket packet);
|
||||
|
||||
/**
|
||||
* Queue a packet to this connection.
|
||||
* If the packet is not registered for the connections current encoder protocol, it will be queued until it is,
|
||||
* otherwise it will be sent immediately.
|
||||
*
|
||||
* @param packet the packet to be queued
|
||||
* @throws UnsupportedOperationException if used for a PendingConnection
|
||||
*/
|
||||
void sendPacketQueued(DefinedPacket packet);
|
||||
}
|
||||
}
|
||||
|
@@ -113,4 +113,18 @@ public interface PendingConnection extends Connection
|
||||
*/
|
||||
@ApiStatus.Experimental
|
||||
CompletableFuture<byte[]> retrieveCookie(String cookie);
|
||||
|
||||
/**
|
||||
* Sends a login payload request to the client.
|
||||
*
|
||||
* @param channel the channel to send this data via
|
||||
* @param data the data to send
|
||||
* @return a {@link CompletableFuture} that will be completed when the Login
|
||||
* Payload response is received. If the Vanilla client doesn't know the
|
||||
* channel, the {@link CompletableFuture} will complete with a null value
|
||||
* @throws IllegalStateException if the player's version is not at least
|
||||
* 1.13
|
||||
*/
|
||||
@ApiStatus.Experimental
|
||||
CompletableFuture<byte[]> sendData(String channel, byte[] data);
|
||||
}
|
||||
|
@@ -12,6 +12,7 @@ import net.md_5.bungee.api.SkinConfiguration;
|
||||
import net.md_5.bungee.api.Title;
|
||||
import net.md_5.bungee.api.chat.BaseComponent;
|
||||
import net.md_5.bungee.api.config.ServerInfo;
|
||||
import net.md_5.bungee.api.dialog.Dialog;
|
||||
import net.md_5.bungee.api.event.ServerConnectEvent;
|
||||
import net.md_5.bungee.api.score.Scoreboard;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
@@ -382,4 +383,31 @@ public interface ProxiedPlayer extends Connection, CommandSender
|
||||
*/
|
||||
@ApiStatus.Experimental
|
||||
void transfer(String host, int port);
|
||||
|
||||
/**
|
||||
* Gets the client brand of this player.
|
||||
* If the player has not sent a brand packet yet, it will return null.
|
||||
*
|
||||
* @return the brand of the client, or null if not received yet
|
||||
*/
|
||||
String getClientBrand();
|
||||
|
||||
/**
|
||||
* Clear the player's open dialog.
|
||||
*
|
||||
* @throws IllegalStateException if the players version is not at least
|
||||
* 1.21.6
|
||||
*/
|
||||
@ApiStatus.Experimental
|
||||
void clearDialog();
|
||||
|
||||
/**
|
||||
* Show a dialog to the player.
|
||||
*
|
||||
* @param dialog the dialog to show
|
||||
* @throws IllegalStateException if the players version is not at least
|
||||
* 1.21.6
|
||||
*/
|
||||
@ApiStatus.Experimental
|
||||
void showDialog(Dialog dialog);
|
||||
}
|
||||
|
@@ -0,0 +1,39 @@
|
||||
package net.md_5.bungee.api.event;
|
||||
|
||||
import com.google.gson.JsonElement;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.ToString;
|
||||
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
||||
import net.md_5.bungee.api.plugin.Cancellable;
|
||||
import net.md_5.bungee.api.plugin.Event;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
|
||||
/**
|
||||
* Called after a {@link ProxiedPlayer} runs a custom action from a chat event
|
||||
* or form submission.
|
||||
*/
|
||||
@Data
|
||||
@ToString(callSuper = false)
|
||||
@EqualsAndHashCode(callSuper = false)
|
||||
@ApiStatus.Experimental
|
||||
public class CustomClickEvent extends Event implements Cancellable
|
||||
{
|
||||
|
||||
/**
|
||||
* Player who clicked.
|
||||
*/
|
||||
private final ProxiedPlayer player;
|
||||
/**
|
||||
* Custom action ID.
|
||||
*/
|
||||
private final String id;
|
||||
/**
|
||||
* The data as submitted.
|
||||
*/
|
||||
private final JsonElement data;
|
||||
/**
|
||||
* Cancelled state.
|
||||
*/
|
||||
private boolean cancelled;
|
||||
}
|
@@ -3,6 +3,7 @@ package net.md_5.bungee.api.event;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.Getter;
|
||||
import lombok.ToString;
|
||||
import net.md_5.bungee.api.CommandSender;
|
||||
import net.md_5.bungee.api.plugin.Event;
|
||||
|
||||
@@ -10,6 +11,7 @@ import net.md_5.bungee.api.plugin.Event;
|
||||
* Called when somebody reloads BungeeCord
|
||||
*/
|
||||
@Getter
|
||||
@ToString(callSuper = false)
|
||||
@AllArgsConstructor
|
||||
@EqualsAndHashCode(callSuper = false)
|
||||
public class ProxyReloadEvent extends Event
|
||||
|
@@ -35,6 +35,7 @@ import org.eclipse.aether.transport.http.HttpTransporterFactory;
|
||||
class LibraryLoader
|
||||
{
|
||||
|
||||
private static final String REPOSITORY_PROPERTY = "net.md_5.bungee.api.plugin.centralURL";
|
||||
private final Logger logger;
|
||||
private final RepositorySystem repository;
|
||||
private final DefaultRepositorySystemSession session;
|
||||
@@ -68,7 +69,7 @@ class LibraryLoader
|
||||
session.setSystemProperties( System.getProperties() );
|
||||
session.setReadOnly();
|
||||
|
||||
this.repositories = repository.newResolutionRepositories( session, Arrays.asList( new RemoteRepository.Builder( "central", "default", "https://repo.maven.apache.org/maven2" ).build() ) );
|
||||
this.repositories = repository.newResolutionRepositories( session, Arrays.asList( new RemoteRepository.Builder( "central", "default", System.getProperty( REPOSITORY_PROPERTY, "https://repo.maven.apache.org/maven2" ) ).build() ) );
|
||||
}
|
||||
|
||||
public ClassLoader createLoader(PluginDescription desc)
|
||||
|
@@ -23,6 +23,10 @@ import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.Stack;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import java.util.concurrent.locks.ReadWriteLock;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||
import java.util.jar.JarEntry;
|
||||
import java.util.jar.JarFile;
|
||||
import java.util.logging.Level;
|
||||
@@ -59,6 +63,9 @@ public final class PluginManager
|
||||
private final Multimap<Plugin, Command> commandsByPlugin = ArrayListMultimap.create();
|
||||
private final Multimap<Plugin, Listener> listenersByPlugin = ArrayListMultimap.create();
|
||||
|
||||
private final ReadWriteLock commandsLock = new ReentrantReadWriteLock();
|
||||
private final Lock listenersLock = new ReentrantLock();
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public PluginManager(ProxyServer proxy)
|
||||
{
|
||||
@@ -92,6 +99,9 @@ public final class PluginManager
|
||||
* @param command the command to register
|
||||
*/
|
||||
public void registerCommand(Plugin plugin, Command command)
|
||||
{
|
||||
commandsLock.writeLock().lock();
|
||||
try
|
||||
{
|
||||
commandMap.put( command.getName().toLowerCase( Locale.ROOT ), command );
|
||||
for ( String alias : command.getAliases() )
|
||||
@@ -99,6 +109,10 @@ public final class PluginManager
|
||||
commandMap.put( alias.toLowerCase( Locale.ROOT ), command );
|
||||
}
|
||||
commandsByPlugin.put( plugin, command );
|
||||
} finally
|
||||
{
|
||||
commandsLock.writeLock().unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -107,9 +121,16 @@ public final class PluginManager
|
||||
* @param command the command to unregister
|
||||
*/
|
||||
public void unregisterCommand(Command command)
|
||||
{
|
||||
commandsLock.writeLock().lock();
|
||||
try
|
||||
{
|
||||
while ( commandMap.values().remove( command ) );
|
||||
commandsByPlugin.values().remove( command );
|
||||
} finally
|
||||
{
|
||||
commandsLock.writeLock().unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -118,6 +139,9 @@ public final class PluginManager
|
||||
* @param plugin the plugin to register the commands of
|
||||
*/
|
||||
public void unregisterCommands(Plugin plugin)
|
||||
{
|
||||
commandsLock.writeLock().lock();
|
||||
try
|
||||
{
|
||||
for ( Iterator<Command> it = commandsByPlugin.get( plugin ).iterator(); it.hasNext(); )
|
||||
{
|
||||
@@ -125,6 +149,10 @@ public final class PluginManager
|
||||
while ( commandMap.values().remove( command ) );
|
||||
it.remove();
|
||||
}
|
||||
} finally
|
||||
{
|
||||
commandsLock.writeLock().unlock();
|
||||
}
|
||||
}
|
||||
|
||||
private Command getCommandIfEnabled(String commandName, CommandSender sender)
|
||||
@@ -137,7 +165,14 @@ public final class PluginManager
|
||||
return null;
|
||||
}
|
||||
|
||||
commandsLock.readLock().lock();
|
||||
try
|
||||
{
|
||||
return commandMap.get( commandLower );
|
||||
} finally
|
||||
{
|
||||
commandsLock.readLock().unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -433,6 +468,9 @@ public final class PluginManager
|
||||
* @param listener the listener to register events for
|
||||
*/
|
||||
public void registerListener(Plugin plugin, Listener listener)
|
||||
{
|
||||
listenersLock.lock();
|
||||
try
|
||||
{
|
||||
for ( Method method : listener.getClass().getDeclaredMethods() )
|
||||
{
|
||||
@@ -441,6 +479,10 @@ public final class PluginManager
|
||||
}
|
||||
eventBus.register( listener );
|
||||
listenersByPlugin.put( plugin, listener );
|
||||
} finally
|
||||
{
|
||||
listenersLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -449,9 +491,16 @@ public final class PluginManager
|
||||
* @param listener the listener to unregister
|
||||
*/
|
||||
public void unregisterListener(Listener listener)
|
||||
{
|
||||
listenersLock.lock();
|
||||
try
|
||||
{
|
||||
eventBus.unregister( listener );
|
||||
listenersByPlugin.values().remove( listener );
|
||||
} finally
|
||||
{
|
||||
listenersLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -460,12 +509,19 @@ public final class PluginManager
|
||||
* @param plugin target plugin
|
||||
*/
|
||||
public void unregisterListeners(Plugin plugin)
|
||||
{
|
||||
listenersLock.lock();
|
||||
try
|
||||
{
|
||||
for ( Iterator<Listener> it = listenersByPlugin.get( plugin ).iterator(); it.hasNext(); )
|
||||
{
|
||||
eventBus.unregister( it.next() );
|
||||
it.remove();
|
||||
}
|
||||
} finally
|
||||
{
|
||||
listenersLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -474,8 +530,15 @@ public final class PluginManager
|
||||
* @return commands
|
||||
*/
|
||||
public Collection<Map.Entry<String, Command>> getCommands()
|
||||
{
|
||||
commandsLock.readLock().lock();
|
||||
try
|
||||
{
|
||||
return Collections.unmodifiableCollection( commandMap.entrySet() );
|
||||
} finally
|
||||
{
|
||||
commandsLock.readLock().unlock();
|
||||
}
|
||||
}
|
||||
|
||||
boolean isTransitiveDepend(PluginDescription plugin, PluginDescription depend)
|
||||
|
@@ -1,22 +1,37 @@
|
||||
package net.md_5.bungee.util;
|
||||
|
||||
import gnu.trove.strategy.HashingStrategy;
|
||||
import it.unimi.dsi.fastutil.Hash;
|
||||
import java.util.Locale;
|
||||
|
||||
class CaseInsensitiveHashingStrategy implements HashingStrategy
|
||||
class CaseInsensitiveHashingStrategy implements Hash.Strategy<String>
|
||||
{
|
||||
|
||||
static final CaseInsensitiveHashingStrategy INSTANCE = new CaseInsensitiveHashingStrategy();
|
||||
|
||||
@Override
|
||||
public int computeHashCode(Object object)
|
||||
public int hashCode(String object)
|
||||
{
|
||||
return ( (String) object ).toLowerCase( Locale.ROOT ).hashCode();
|
||||
if ( object == null )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return object.toLowerCase( Locale.ROOT ).hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o1, Object o2)
|
||||
public boolean equals(String o1, String o2)
|
||||
{
|
||||
return o1.equals( o2 ) || ( o1 instanceof String && o2 instanceof String && ( (String) o1 ).toLowerCase( Locale.ROOT ).equals( ( (String) o2 ).toLowerCase( Locale.ROOT ) ) );
|
||||
if ( o1 == o2 )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if ( o1 == null || o2 == null )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return o1.equals( o2 ) || o1.toLowerCase( Locale.ROOT ).equals( o2.toLowerCase( Locale.ROOT ) );
|
||||
}
|
||||
}
|
||||
|
@@ -1,9 +1,9 @@
|
||||
package net.md_5.bungee.util;
|
||||
|
||||
import gnu.trove.map.hash.TCustomHashMap;
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenCustomHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class CaseInsensitiveMap<V> extends TCustomHashMap<String, V>
|
||||
public class CaseInsensitiveMap<V> extends Object2ObjectOpenCustomHashMap<String, V>
|
||||
{
|
||||
|
||||
public CaseInsensitiveMap()
|
||||
@@ -13,6 +13,6 @@ public class CaseInsensitiveMap<V> extends TCustomHashMap<String, V>
|
||||
|
||||
public CaseInsensitiveMap(Map<? extends String, ? extends V> map)
|
||||
{
|
||||
super( CaseInsensitiveHashingStrategy.INSTANCE, map );
|
||||
super( map, CaseInsensitiveHashingStrategy.INSTANCE );
|
||||
}
|
||||
}
|
||||
|
@@ -1,9 +1,9 @@
|
||||
package net.md_5.bungee.util;
|
||||
|
||||
import gnu.trove.set.hash.TCustomHashSet;
|
||||
import it.unimi.dsi.fastutil.objects.ObjectOpenCustomHashSet;
|
||||
import java.util.Collection;
|
||||
|
||||
public class CaseInsensitiveSet extends TCustomHashSet<String>
|
||||
public class CaseInsensitiveSet extends ObjectOpenCustomHashSet<String>
|
||||
{
|
||||
|
||||
public CaseInsensitiveSet()
|
||||
@@ -13,6 +13,6 @@ public class CaseInsensitiveSet extends TCustomHashSet<String>
|
||||
|
||||
public CaseInsensitiveSet(Collection<? extends String> collection)
|
||||
{
|
||||
super( CaseInsensitiveHashingStrategy.INSTANCE, collection );
|
||||
super( collection, CaseInsensitiveHashingStrategy.INSTANCE );
|
||||
}
|
||||
}
|
||||
|
@@ -13,12 +13,12 @@ public class CaseInsensitiveTest
|
||||
CaseInsensitiveMap<Object> map = new CaseInsensitiveMap<>();
|
||||
|
||||
map.put( "FOO", obj );
|
||||
assertTrue( map.contains( "foo" ) ); // Assert that contains is case insensitive
|
||||
assertTrue( map.containsKey( "foo" ) ); // Assert that contains is case insensitive
|
||||
assertTrue( map.entrySet().iterator().next().getKey().equals( "FOO" ) ); // Assert that case is preserved
|
||||
|
||||
// Assert that remove is case insensitive
|
||||
map.remove( "FoO" );
|
||||
assertFalse( map.contains( "foo" ) );
|
||||
assertFalse( map.containsKey( "foo" ) );
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@@ -6,13 +6,13 @@
|
||||
<parent>
|
||||
<groupId>net.md-5</groupId>
|
||||
<artifactId>bungeecord-parent</artifactId>
|
||||
<version>1.21-R0.1-SNAPSHOT</version>
|
||||
<version>1.21-R0.4-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
<groupId>net.md-5</groupId>
|
||||
<artifactId>bungeecord-bootstrap</artifactId>
|
||||
<version>1.21-R0.1-SNAPSHOT</version>
|
||||
<version>1.21-R0.4-SNAPSHOT</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>BungeeCord-Bootstrap</name>
|
||||
@@ -48,6 +48,7 @@
|
||||
<Main-Class>net.md_5.bungee.Bootstrap</Main-Class>
|
||||
<Implementation-Version>${describe}</Implementation-Version>
|
||||
<Specification-Version>${maven.build.timestamp}</Specification-Version>
|
||||
<Enable-Native-Access>ALL-UNNAMED</Enable-Native-Access>
|
||||
</manifestEntries>
|
||||
</archive>
|
||||
</configuration>
|
||||
|
@@ -6,13 +6,13 @@
|
||||
<parent>
|
||||
<groupId>net.md-5</groupId>
|
||||
<artifactId>bungeecord-parent</artifactId>
|
||||
<version>1.21-R0.1-SNAPSHOT</version>
|
||||
<version>1.21-R0.4-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
<groupId>net.md-5</groupId>
|
||||
<artifactId>bungeecord-chat</artifactId>
|
||||
<version>1.21-R0.1-SNAPSHOT</version>
|
||||
<version>1.21-R0.4-SNAPSHOT</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>BungeeCord-Chat</name>
|
||||
@@ -22,7 +22,7 @@
|
||||
<dependency>
|
||||
<groupId>com.google.code.gson</groupId>
|
||||
<artifactId>gson</artifactId>
|
||||
<version>2.10.1</version>
|
||||
<version>2.11.0</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
@@ -1,8 +1,10 @@
|
||||
package net.md_5.bungee.api.chat;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
@@ -129,6 +131,10 @@ public abstract class BaseComponent
|
||||
{
|
||||
setColor( component.getColorRaw() );
|
||||
}
|
||||
if ( replace || !style.hasShadowColor() )
|
||||
{
|
||||
setShadowColor( component.getShadowColorRaw() );
|
||||
}
|
||||
if ( replace || !style.hasFont() )
|
||||
{
|
||||
setFont( component.getFontRaw() );
|
||||
@@ -175,6 +181,7 @@ public abstract class BaseComponent
|
||||
if ( retention == FormatRetention.EVENTS || retention == FormatRetention.NONE )
|
||||
{
|
||||
setColor( null );
|
||||
setShadowColor( null );
|
||||
setBold( null );
|
||||
setItalic( null );
|
||||
setUnderlined( null );
|
||||
@@ -295,6 +302,46 @@ public abstract class BaseComponent
|
||||
return style.getColor();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set this component's shadow color.
|
||||
*
|
||||
* @param color the component shadow color, or null to use the default
|
||||
*/
|
||||
public void setShadowColor(Color color)
|
||||
{
|
||||
this.style.setShadowColor( color );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the shadow color of this component. This uses the parent's shadow color if this
|
||||
* component doesn't have one. null is returned if no shadow color is found.
|
||||
*
|
||||
* @return the shadow color of this component
|
||||
*/
|
||||
public Color getShadowColor()
|
||||
{
|
||||
if ( !style.hasShadowColor() )
|
||||
{
|
||||
if ( parent == null )
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return parent.getShadowColor();
|
||||
}
|
||||
return style.getShadowColor();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the shadow color of this component without checking the parents
|
||||
* shadow color. May return null
|
||||
*
|
||||
* @return the shadow color of this component
|
||||
*/
|
||||
public Color getShadowColorRaw()
|
||||
{
|
||||
return style.getShadowColor();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set this component's font.
|
||||
*
|
||||
@@ -536,6 +583,10 @@ public abstract class BaseComponent
|
||||
{
|
||||
setColor( style.getColor() );
|
||||
}
|
||||
if ( style.hasShadowColor() )
|
||||
{
|
||||
setShadowColor( style.getShadowColor() );
|
||||
}
|
||||
if ( style.hasFont() )
|
||||
{
|
||||
setFont( style.getFont() );
|
||||
@@ -627,11 +678,11 @@ public abstract class BaseComponent
|
||||
public String toPlainText()
|
||||
{
|
||||
StringBuilder builder = new StringBuilder();
|
||||
toPlainText( builder );
|
||||
toPlainText( new LimitedStringVisitor( builder, Short.MAX_VALUE ) );
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
void toPlainText(StringBuilder builder)
|
||||
void toPlainText(StringVisitor builder)
|
||||
{
|
||||
if ( extra != null )
|
||||
{
|
||||
@@ -651,11 +702,11 @@ public abstract class BaseComponent
|
||||
public String toLegacyText()
|
||||
{
|
||||
StringBuilder builder = new StringBuilder();
|
||||
toLegacyText( builder );
|
||||
toLegacyText( new LimitedStringVisitor( builder, Short.MAX_VALUE ) );
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
void toLegacyText(StringBuilder builder)
|
||||
void toLegacyText(StringVisitor builder)
|
||||
{
|
||||
if ( extra != null )
|
||||
{
|
||||
@@ -666,7 +717,7 @@ public abstract class BaseComponent
|
||||
}
|
||||
}
|
||||
|
||||
void addFormat(StringBuilder builder)
|
||||
void addFormat(StringVisitor builder)
|
||||
{
|
||||
builder.append( getColor() );
|
||||
if ( isBold() )
|
||||
@@ -690,4 +741,35 @@ public abstract class BaseComponent
|
||||
builder.append( ChatColor.MAGIC );
|
||||
}
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
protected static interface StringVisitor
|
||||
{
|
||||
|
||||
void append(String s);
|
||||
|
||||
default void append(Object obj)
|
||||
{
|
||||
append( String.valueOf( obj ) );
|
||||
}
|
||||
}
|
||||
|
||||
@Data
|
||||
protected static class LimitedStringVisitor implements StringVisitor
|
||||
{
|
||||
|
||||
private final StringBuilder builder;
|
||||
private final int maxLength;
|
||||
|
||||
@Override
|
||||
public void append(String s)
|
||||
{
|
||||
if ( builder.length() >= maxLength )
|
||||
{
|
||||
throw new IllegalArgumentException( "String exceeded maximum length " + maxLength );
|
||||
}
|
||||
|
||||
builder.append( s );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -4,12 +4,14 @@ import lombok.EqualsAndHashCode;
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.ToString;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
|
||||
@Getter
|
||||
@ToString
|
||||
@EqualsAndHashCode
|
||||
@RequiredArgsConstructor
|
||||
public final class ClickEvent
|
||||
@ApiStatus.NonExtendable
|
||||
public class ClickEvent
|
||||
{
|
||||
|
||||
/**
|
||||
@@ -52,11 +54,19 @@ public final class ClickEvent
|
||||
* {@link net.md_5.bungee.api.chat.ClickEvent#value} in a book.
|
||||
*/
|
||||
CHANGE_PAGE,
|
||||
/**
|
||||
* Must use subclass ShowDialogClickEvent.
|
||||
*/
|
||||
SHOW_DIALOG,
|
||||
/**
|
||||
* Copy the string given by
|
||||
* {@link net.md_5.bungee.api.chat.ClickEvent#value} into the player's
|
||||
* clipboard.
|
||||
*/
|
||||
COPY_TO_CLIPBOARD
|
||||
COPY_TO_CLIPBOARD,
|
||||
/**
|
||||
* Must use subclass {@link ClickEventCustom}.
|
||||
*/
|
||||
CUSTOM,
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,30 @@
|
||||
package net.md_5.bungee.api.chat;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.ToString;
|
||||
|
||||
/**
|
||||
* Click event which sends a custom payload to the server.
|
||||
*/
|
||||
@Data
|
||||
@ToString(callSuper = true)
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class ClickEventCustom extends ClickEvent
|
||||
{
|
||||
|
||||
/**
|
||||
* The custom payload.
|
||||
*/
|
||||
private final String payload;
|
||||
|
||||
/**
|
||||
* @param id identifier for the event (lower case, no special characters)
|
||||
* @param payload custom payload
|
||||
*/
|
||||
public ClickEventCustom(String id, String payload)
|
||||
{
|
||||
super( ClickEvent.Action.CUSTOM, id );
|
||||
this.payload = payload;
|
||||
}
|
||||
}
|
@@ -1,5 +1,6 @@
|
||||
package net.md_5.bungee.api.chat;
|
||||
|
||||
import java.awt.Color;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.NoArgsConstructor;
|
||||
@@ -23,6 +24,10 @@ public final class ComponentStyle implements Cloneable
|
||||
* {@link ChatColor#color} should not be null).</b>
|
||||
*/
|
||||
private ChatColor color;
|
||||
/**
|
||||
* The shadow color of this style.
|
||||
*/
|
||||
private Color shadowColor;
|
||||
/**
|
||||
* The font of this style.
|
||||
*/
|
||||
@@ -68,6 +73,26 @@ public final class ComponentStyle implements Cloneable
|
||||
return ( color != null );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the shadow color of this style. May return null.
|
||||
*
|
||||
* @return the shadow color of this style, or null if default color
|
||||
*/
|
||||
public Color getShadowColor()
|
||||
{
|
||||
return shadowColor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether or not this style has a shadow color set.
|
||||
*
|
||||
* @return whether a shadow color is set
|
||||
*/
|
||||
public boolean hasShadowColor()
|
||||
{
|
||||
return ( shadowColor != null );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the font of this style. May return null.
|
||||
*
|
||||
@@ -195,7 +220,7 @@ public final class ComponentStyle implements Cloneable
|
||||
*/
|
||||
public boolean isEmpty()
|
||||
{
|
||||
return color == null && font == null && bold == null
|
||||
return color == null && shadowColor == null && font == null && bold == null
|
||||
&& italic == null && underlined == null
|
||||
&& strikethrough == null && obfuscated == null;
|
||||
}
|
||||
@@ -203,7 +228,7 @@ public final class ComponentStyle implements Cloneable
|
||||
@Override
|
||||
public ComponentStyle clone()
|
||||
{
|
||||
return new ComponentStyle( color, font, bold, italic, underlined, strikethrough, obfuscated );
|
||||
return new ComponentStyle( color, shadowColor, font, bold, italic, underlined, strikethrough, obfuscated );
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -227,6 +252,7 @@ public final class ComponentStyle implements Cloneable
|
||||
{
|
||||
return new ComponentStyleBuilder()
|
||||
.color( other.color )
|
||||
.shadowColor( other.shadowColor )
|
||||
.font( other.font )
|
||||
.bold( other.bold )
|
||||
.italic( other.italic )
|
||||
|
@@ -1,5 +1,6 @@
|
||||
package net.md_5.bungee.api.chat;
|
||||
|
||||
import java.awt.Color;
|
||||
import net.md_5.bungee.api.ChatColor;
|
||||
|
||||
/**
|
||||
@@ -26,6 +27,7 @@ public final class ComponentStyleBuilder
|
||||
{
|
||||
|
||||
private ChatColor color;
|
||||
private Color shadowColor;
|
||||
private String font;
|
||||
private Boolean bold, italic, underlined, strikethrough, obfuscated;
|
||||
|
||||
@@ -41,6 +43,18 @@ public final class ComponentStyleBuilder
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the style shadow color.
|
||||
*
|
||||
* @param shadowColor the shadow color to set, or null to use the default
|
||||
* @return this ComponentStyleBuilder for chaining
|
||||
*/
|
||||
public ComponentStyleBuilder shadowColor(Color shadowColor)
|
||||
{
|
||||
this.shadowColor = shadowColor;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the style font.
|
||||
*
|
||||
@@ -121,6 +135,6 @@ public final class ComponentStyleBuilder
|
||||
*/
|
||||
public ComponentStyle build()
|
||||
{
|
||||
return new ComponentStyle( color, font, bold, italic, underlined, strikethrough, obfuscated );
|
||||
return new ComponentStyle( color, shadowColor, font, bold, italic, underlined, strikethrough, obfuscated );
|
||||
}
|
||||
}
|
||||
|
@@ -13,7 +13,7 @@ import net.md_5.bungee.api.chat.hover.content.Content;
|
||||
import net.md_5.bungee.api.chat.hover.content.Entity;
|
||||
import net.md_5.bungee.api.chat.hover.content.Item;
|
||||
import net.md_5.bungee.api.chat.hover.content.Text;
|
||||
import net.md_5.bungee.chat.ComponentSerializer;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
|
||||
@Getter
|
||||
@ToString
|
||||
@@ -34,6 +34,7 @@ public final class HoverEvent
|
||||
* Returns whether this hover event is prior to 1.16
|
||||
*/
|
||||
@Setter
|
||||
@ApiStatus.Internal
|
||||
private boolean legacy = false;
|
||||
|
||||
/**
|
||||
@@ -71,22 +72,6 @@ public final class HoverEvent
|
||||
this.legacy = true;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public BaseComponent[] getValue()
|
||||
{
|
||||
Content content = contents.get( 0 );
|
||||
if ( content instanceof Text && ( (Text) content ).getValue() instanceof BaseComponent[] )
|
||||
{
|
||||
return (BaseComponent[]) ( (Text) content ).getValue();
|
||||
}
|
||||
|
||||
TextComponent component = new TextComponent( ComponentSerializer.toString( content ) );
|
||||
return new BaseComponent[]
|
||||
{
|
||||
component
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a content to this hover event.
|
||||
*
|
||||
|
@@ -50,14 +50,14 @@ public final class KeybindComponent extends BaseComponent
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void toPlainText(StringBuilder builder)
|
||||
protected void toPlainText(StringVisitor builder)
|
||||
{
|
||||
builder.append( getKeybind() );
|
||||
super.toPlainText( builder );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void toLegacyText(StringBuilder builder)
|
||||
protected void toLegacyText(StringVisitor builder)
|
||||
{
|
||||
addFormat( builder );
|
||||
builder.append( getKeybind() );
|
||||
|
@@ -85,14 +85,14 @@ public final class ScoreComponent extends BaseComponent
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void toPlainText(StringBuilder builder)
|
||||
protected void toPlainText(StringVisitor builder)
|
||||
{
|
||||
builder.append( this.value );
|
||||
super.toPlainText( builder );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void toLegacyText(StringBuilder builder)
|
||||
protected void toLegacyText(StringVisitor builder)
|
||||
{
|
||||
addFormat( builder );
|
||||
builder.append( this.value );
|
||||
|
@@ -69,14 +69,14 @@ public final class SelectorComponent extends BaseComponent
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void toPlainText(StringBuilder builder)
|
||||
protected void toPlainText(StringVisitor builder)
|
||||
{
|
||||
builder.append( this.selector );
|
||||
super.toPlainText( builder );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void toLegacyText(StringBuilder builder)
|
||||
protected void toLegacyText(StringVisitor builder)
|
||||
{
|
||||
addFormat( builder );
|
||||
builder.append( this.selector );
|
||||
|
@@ -280,14 +280,14 @@ public final class TextComponent extends BaseComponent
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void toPlainText(StringBuilder builder)
|
||||
protected void toPlainText(StringVisitor builder)
|
||||
{
|
||||
builder.append( text );
|
||||
super.toPlainText( builder );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void toLegacyText(StringBuilder builder)
|
||||
protected void toLegacyText(StringVisitor builder)
|
||||
{
|
||||
addFormat( builder );
|
||||
builder.append( text );
|
||||
|
@@ -156,20 +156,20 @@ public final class TranslatableComponent extends BaseComponent
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void toPlainText(StringBuilder builder)
|
||||
protected void toPlainText(StringVisitor builder)
|
||||
{
|
||||
convert( builder, false );
|
||||
super.toPlainText( builder );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void toLegacyText(StringBuilder builder)
|
||||
protected void toLegacyText(StringVisitor builder)
|
||||
{
|
||||
convert( builder, true );
|
||||
super.toLegacyText( builder );
|
||||
}
|
||||
|
||||
private void convert(StringBuilder builder, boolean applyFormat)
|
||||
private void convert(StringVisitor builder, boolean applyFormat)
|
||||
{
|
||||
String trans = TranslationRegistry.INSTANCE.translate( translate );
|
||||
|
||||
|
@@ -1,153 +0,0 @@
|
||||
package net.md_5.bungee.chat;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.gson.JsonDeserializationContext;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonSerializationContext;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.IdentityHashMap;
|
||||
import java.util.Locale;
|
||||
import net.md_5.bungee.api.chat.BaseComponent;
|
||||
import net.md_5.bungee.api.chat.ClickEvent;
|
||||
import net.md_5.bungee.api.chat.ComponentStyle;
|
||||
import net.md_5.bungee.api.chat.HoverEvent;
|
||||
import net.md_5.bungee.api.chat.hover.content.Content;
|
||||
|
||||
public class BaseComponentSerializer
|
||||
{
|
||||
|
||||
protected void deserialize(JsonObject object, BaseComponent component, JsonDeserializationContext context)
|
||||
{
|
||||
component.applyStyle( context.deserialize( object, ComponentStyle.class ) );
|
||||
|
||||
JsonElement insertion = object.get( "insertion" );
|
||||
if ( insertion != null )
|
||||
{
|
||||
component.setInsertion( insertion.getAsString() );
|
||||
}
|
||||
|
||||
//Events
|
||||
JsonObject clickEvent = object.getAsJsonObject( "clickEvent" );
|
||||
if ( clickEvent != null )
|
||||
{
|
||||
component.setClickEvent( new ClickEvent(
|
||||
ClickEvent.Action.valueOf( clickEvent.get( "action" ).getAsString().toUpperCase( Locale.ROOT ) ),
|
||||
( clickEvent.has( "value" ) ) ? clickEvent.get( "value" ).getAsString() : "" ) );
|
||||
}
|
||||
JsonObject hoverEventJson = object.getAsJsonObject( "hoverEvent" );
|
||||
if ( hoverEventJson != null )
|
||||
{
|
||||
HoverEvent hoverEvent = null;
|
||||
HoverEvent.Action action = HoverEvent.Action.valueOf( hoverEventJson.get( "action" ).getAsString().toUpperCase( Locale.ROOT ) );
|
||||
|
||||
JsonElement value = hoverEventJson.get( "value" );
|
||||
if ( value != null )
|
||||
{
|
||||
|
||||
// Plugins previously had support to pass BaseComponent[] into any action.
|
||||
// If the GSON is possible to be parsed as BaseComponent, attempt to parse as so.
|
||||
BaseComponent[] components;
|
||||
if ( value.isJsonArray() )
|
||||
{
|
||||
components = context.deserialize( value, BaseComponent[].class );
|
||||
} else
|
||||
{
|
||||
components = new BaseComponent[]
|
||||
{
|
||||
context.deserialize( value, BaseComponent.class )
|
||||
};
|
||||
}
|
||||
hoverEvent = new HoverEvent( action, components );
|
||||
} else
|
||||
{
|
||||
JsonElement contents = hoverEventJson.get( "contents" );
|
||||
if ( contents != null )
|
||||
{
|
||||
Content[] list;
|
||||
if ( contents.isJsonArray() )
|
||||
{
|
||||
list = context.deserialize( contents, HoverEvent.getClass( action, true ) );
|
||||
} else
|
||||
{
|
||||
list = new Content[]
|
||||
{
|
||||
context.deserialize( contents, HoverEvent.getClass( action, false ) )
|
||||
};
|
||||
}
|
||||
hoverEvent = new HoverEvent( action, new ArrayList<>( Arrays.asList( list ) ) );
|
||||
}
|
||||
}
|
||||
|
||||
if ( hoverEvent != null )
|
||||
{
|
||||
component.setHoverEvent( hoverEvent );
|
||||
}
|
||||
}
|
||||
|
||||
JsonElement extra = object.get( "extra" );
|
||||
if ( extra != null )
|
||||
{
|
||||
component.setExtra( Arrays.asList( context.deserialize( extra, BaseComponent[].class ) ) );
|
||||
}
|
||||
}
|
||||
|
||||
protected void serialize(JsonObject object, BaseComponent component, JsonSerializationContext context)
|
||||
{
|
||||
boolean first = false;
|
||||
if ( ComponentSerializer.serializedComponents.get() == null )
|
||||
{
|
||||
first = true;
|
||||
ComponentSerializer.serializedComponents.set( Collections.newSetFromMap( new IdentityHashMap<BaseComponent, Boolean>() ) );
|
||||
}
|
||||
try
|
||||
{
|
||||
Preconditions.checkArgument( !ComponentSerializer.serializedComponents.get().contains( component ), "Component loop" );
|
||||
ComponentSerializer.serializedComponents.get().add( component );
|
||||
|
||||
ComponentStyleSerializer.serializeTo( component.getStyle(), object );
|
||||
|
||||
if ( component.getInsertion() != null )
|
||||
{
|
||||
object.addProperty( "insertion", component.getInsertion() );
|
||||
}
|
||||
|
||||
//Events
|
||||
if ( component.getClickEvent() != null )
|
||||
{
|
||||
JsonObject clickEvent = new JsonObject();
|
||||
clickEvent.addProperty( "action", component.getClickEvent().getAction().toString().toLowerCase( Locale.ROOT ) );
|
||||
clickEvent.addProperty( "value", component.getClickEvent().getValue() );
|
||||
object.add( "clickEvent", clickEvent );
|
||||
}
|
||||
if ( component.getHoverEvent() != null )
|
||||
{
|
||||
JsonObject hoverEvent = new JsonObject();
|
||||
hoverEvent.addProperty( "action", component.getHoverEvent().getAction().toString().toLowerCase( Locale.ROOT ) );
|
||||
if ( component.getHoverEvent().isLegacy() )
|
||||
{
|
||||
hoverEvent.add( "value", context.serialize( component.getHoverEvent().getContents().get( 0 ) ) );
|
||||
} else
|
||||
{
|
||||
hoverEvent.add( "contents", context.serialize( ( component.getHoverEvent().getContents().size() == 1 )
|
||||
? component.getHoverEvent().getContents().get( 0 ) : component.getHoverEvent().getContents() ) );
|
||||
}
|
||||
object.add( "hoverEvent", hoverEvent );
|
||||
}
|
||||
|
||||
if ( component.getExtra() != null )
|
||||
{
|
||||
object.add( "extra", context.serialize( component.getExtra() ) );
|
||||
}
|
||||
} finally
|
||||
{
|
||||
ComponentSerializer.serializedComponents.get().remove( component );
|
||||
if ( first )
|
||||
{
|
||||
ComponentSerializer.serializedComponents.set( null );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -6,13 +6,13 @@
|
||||
<parent>
|
||||
<groupId>net.md-5</groupId>
|
||||
<artifactId>bungeecord-parent</artifactId>
|
||||
<version>1.21-R0.1-SNAPSHOT</version>
|
||||
<version>1.21-R0.4-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
<groupId>net.md-5</groupId>
|
||||
<artifactId>bungeecord-config</artifactId>
|
||||
<version>1.21-R0.1-SNAPSHOT</version>
|
||||
<version>1.21-R0.4-SNAPSHOT</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>BungeeCord-Config</name>
|
||||
@@ -22,7 +22,7 @@
|
||||
<dependency>
|
||||
<groupId>com.google.code.gson</groupId>
|
||||
<artifactId>gson</artifactId>
|
||||
<version>2.10.1</version>
|
||||
<version>2.11.0</version>
|
||||
<scope>compile</scope>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
28
dialog/LICENSE
Normal file
28
dialog/LICENSE
Normal file
@@ -0,0 +1,28 @@
|
||||
BSD 3-Clause License
|
||||
|
||||
Copyright (c) 2025, SpigotMC Pty. Ltd.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
3. Neither the name of the copyright holder nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
38
dialog/README.md
Normal file
38
dialog/README.md
Normal file
@@ -0,0 +1,38 @@
|
||||
BungeeCord-Dialog
|
||||
=================
|
||||
|
||||
Highly experimental API, subject to breakage. All contributions welcome, including major refactors/design changes.
|
||||
|
||||
Sample Plugin
|
||||
-------------
|
||||
|
||||
```java
|
||||
private class TestCommand extends Command
|
||||
{
|
||||
|
||||
public TestCommand()
|
||||
{
|
||||
super( "btest" );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(CommandSender sender, String[] args)
|
||||
{
|
||||
ProxiedPlayer player = (ProxiedPlayer) sender;
|
||||
|
||||
Dialog notice = new NoticeDialog( new DialogBase( new ComponentBuilder( "Hello" ).color( ChatColor.RED ).build() ) );
|
||||
player.showDialog( notice );
|
||||
|
||||
notice = new NoticeDialog(
|
||||
new DialogBase( new ComponentBuilder( "Hello" ).color( ChatColor.RED ).build() )
|
||||
.inputs(
|
||||
Arrays.asList( new TextInput( "first", new ComponentBuilder( "First" ).build() ),
|
||||
new TextInput( "second", new ComponentBuilder( "Second" ).build() )
|
||||
)
|
||||
) )
|
||||
.action( new ActionButton( new ComponentBuilder( "Submit Button" ).build(), new CustomClickAction( "customform" ) ) );
|
||||
|
||||
player.sendMessage( new ComponentBuilder( "click me" ).event( new ShowDialogClickEvent( notice ) ).build() );
|
||||
}
|
||||
}
|
||||
```
|
31
dialog/nb-configuration.xml
Normal file
31
dialog/nb-configuration.xml
Normal file
@@ -0,0 +1,31 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project-shared-configuration>
|
||||
<!--
|
||||
This file contains additional configuration written by modules in the NetBeans IDE.
|
||||
The configuration is intended to be shared among all the users of project and
|
||||
therefore it is assumed to be part of version control checkout.
|
||||
Without this configuration present, some functionality in the IDE may be limited or fail altogether.
|
||||
-->
|
||||
<properties xmlns="http://www.netbeans.org/ns/maven-properties-data/1">
|
||||
<!--
|
||||
Properties that influence various parts of the IDE, especially code formatting and the like.
|
||||
You can copy and paste the single properties, into the pom.xml file and the IDE will pick them up.
|
||||
That way multiple projects can share the same settings (useful for formatting rules for example).
|
||||
Any value defined here will override the pom.xml file value but is only applicable to the current project.
|
||||
-->
|
||||
<org-netbeans-modules-editor-indent.CodeStyle.usedProfile>project</org-netbeans-modules-editor-indent.CodeStyle.usedProfile>
|
||||
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.classDeclBracePlacement>NEW_LINE</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.classDeclBracePlacement>
|
||||
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.otherBracePlacement>NEW_LINE</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.otherBracePlacement>
|
||||
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.methodDeclBracePlacement>NEW_LINE</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.methodDeclBracePlacement>
|
||||
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinMethodCallParens>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinMethodCallParens>
|
||||
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinSwitchParens>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinSwitchParens>
|
||||
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinCatchParens>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinCatchParens>
|
||||
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinTryParens>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinTryParens>
|
||||
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinSynchronizedParens>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinSynchronizedParens>
|
||||
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinArrayInitBrackets>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinArrayInitBrackets>
|
||||
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinParens>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinParens>
|
||||
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinWhileParens>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinWhileParens>
|
||||
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinIfParens>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinIfParens>
|
||||
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinForParens>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinForParens>
|
||||
</properties>
|
||||
</project-shared-configuration>
|
36
dialog/pom.xml
Normal file
36
dialog/pom.xml
Normal file
@@ -0,0 +1,36 @@
|
||||
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>net.md-5</groupId>
|
||||
<artifactId>bungeecord-parent</artifactId>
|
||||
<version>1.21-R0.4-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
<groupId>net.md-5</groupId>
|
||||
<artifactId>bungeecord-dialog</artifactId>
|
||||
<version>1.21-R0.4-SNAPSHOT</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>BungeeCord-Dialog</name>
|
||||
<description>Minecraft dialog API intended for use with BungeeCord</description>
|
||||
<licenses>
|
||||
<license>
|
||||
<name>BSD-3-Clause</name>
|
||||
<url>https://github.com/SpigotMC/BungeeCord/blob/master/dialog/LICENSE</url>
|
||||
<distribution>repo</distribution>
|
||||
</license>
|
||||
</licenses>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>net.md-5</groupId>
|
||||
<artifactId>bungeecord-chat</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
@@ -0,0 +1,39 @@
|
||||
package net.md_5.bungee.api.dialog;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.NonNull;
|
||||
import lombok.ToString;
|
||||
import lombok.experimental.Accessors;
|
||||
import net.md_5.bungee.api.dialog.action.ActionButton;
|
||||
|
||||
/**
|
||||
* Represents a simple dialog with text and two actions at the bottom (default:
|
||||
* "yes", "no").
|
||||
*/
|
||||
@Data
|
||||
@ToString
|
||||
@EqualsAndHashCode
|
||||
@AllArgsConstructor
|
||||
@Accessors(fluent = true)
|
||||
public final class ConfirmationDialog implements Dialog
|
||||
{
|
||||
|
||||
@NonNull
|
||||
@Accessors(fluent = false)
|
||||
private DialogBase base;
|
||||
/**
|
||||
* The "yes" click action / bottom (appears on the left).
|
||||
*/
|
||||
private ActionButton yes;
|
||||
/**
|
||||
* The "no" click action / bottom (appears on the right).
|
||||
*/
|
||||
private ActionButton no;
|
||||
|
||||
public ConfirmationDialog(@NonNull DialogBase base)
|
||||
{
|
||||
this( base, null, null );
|
||||
}
|
||||
}
|
29
dialog/src/main/java/net/md_5/bungee/api/dialog/Dialog.java
Normal file
29
dialog/src/main/java/net/md_5/bungee/api/dialog/Dialog.java
Normal file
@@ -0,0 +1,29 @@
|
||||
package net.md_5.bungee.api.dialog;
|
||||
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
|
||||
/**
|
||||
* Represents a dialog GUI.
|
||||
*/
|
||||
public interface Dialog
|
||||
{
|
||||
|
||||
/**
|
||||
* Gets the dialog base which contains the dialog title and other options
|
||||
* common to all types of dialogs.
|
||||
*
|
||||
* @return mutable reference to the dialog base
|
||||
*/
|
||||
DialogBase getBase();
|
||||
|
||||
/**
|
||||
* Sets the dialog base.
|
||||
* <br>
|
||||
* For internal use only as this is mandatory and should be specified in the
|
||||
* constructor.
|
||||
*
|
||||
* @param base the new dialog base
|
||||
*/
|
||||
@ApiStatus.Internal
|
||||
void setBase(DialogBase base);
|
||||
}
|
@@ -0,0 +1,84 @@
|
||||
package net.md_5.bungee.api.dialog;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
import java.util.List;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NonNull;
|
||||
import lombok.experimental.Accessors;
|
||||
import net.md_5.bungee.api.chat.BaseComponent;
|
||||
import net.md_5.bungee.api.dialog.body.DialogBody;
|
||||
import net.md_5.bungee.api.dialog.input.DialogInput;
|
||||
|
||||
/**
|
||||
* Represents the title and other options common to all dialogs.
|
||||
*/
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@Accessors(fluent = true)
|
||||
public final class DialogBase
|
||||
{
|
||||
|
||||
/**
|
||||
* The mandatory dialog title.
|
||||
*/
|
||||
@NonNull
|
||||
private BaseComponent title;
|
||||
/**
|
||||
* The name which is used for any buttons leading to this dialog (eg from a
|
||||
* {@link DialogListDialog}). Otherwise defaults to {@link #title}.
|
||||
*/
|
||||
@SerializedName("external_title")
|
||||
private BaseComponent externalTitle;
|
||||
/**
|
||||
* The inputs to the dialog.
|
||||
*/
|
||||
private List<DialogInput> inputs;
|
||||
/**
|
||||
* The body elements which make up this dialog.
|
||||
*/
|
||||
private List<DialogBody> body;
|
||||
/**
|
||||
* Whether this dialog can be closed with the escape key (default: true).
|
||||
*/
|
||||
@SerializedName("can_close_with_escape")
|
||||
private Boolean canCloseWithEscape;
|
||||
/**
|
||||
* Whether this dialog should pause the game in single-player mode (default:
|
||||
* true).
|
||||
*/
|
||||
private Boolean pause;
|
||||
/**
|
||||
* Action to take after the a click or submit action is performed on the
|
||||
* dialog (default: close).
|
||||
*/
|
||||
@SerializedName("after_action")
|
||||
private AfterAction afterAction;
|
||||
|
||||
public DialogBase(@NonNull BaseComponent title)
|
||||
{
|
||||
this( title, null, null, null, null, null, null );
|
||||
}
|
||||
|
||||
/**
|
||||
* Types of action which may be taken after the dialog.
|
||||
*/
|
||||
public enum AfterAction
|
||||
{
|
||||
/**
|
||||
* Close the dialog.
|
||||
*/
|
||||
@SerializedName("close")
|
||||
CLOSE,
|
||||
/**
|
||||
* Do nothing.
|
||||
*/
|
||||
@SerializedName("none")
|
||||
NONE,
|
||||
/**
|
||||
* Show a waiting for response screen.
|
||||
*/
|
||||
@SerializedName("wait_for_response")
|
||||
WAIT_FOR_RESPONSE;
|
||||
}
|
||||
}
|
@@ -0,0 +1,73 @@
|
||||
package net.md_5.bungee.api.dialog;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.NonNull;
|
||||
import lombok.ToString;
|
||||
import lombok.experimental.Accessors;
|
||||
import net.md_5.bungee.api.dialog.action.ActionButton;
|
||||
|
||||
/**
|
||||
* Represents a dialog which contains buttons that link to other dialogs.
|
||||
*/
|
||||
@Data
|
||||
@ToString
|
||||
@EqualsAndHashCode
|
||||
@Accessors(fluent = true)
|
||||
public final class DialogListDialog implements Dialog
|
||||
{
|
||||
|
||||
@NonNull
|
||||
@Accessors(fluent = false)
|
||||
private DialogBase base;
|
||||
/**
|
||||
* The child dialogs behind each button.
|
||||
*/
|
||||
private List<Dialog> dialogs;
|
||||
/**
|
||||
* The {@link ActionButton} activated when the dialog is exited.
|
||||
*/
|
||||
@SerializedName("exit_action")
|
||||
private ActionButton exitAction;
|
||||
/**
|
||||
* The number of columns for the dialog buttons (default: 2).
|
||||
*/
|
||||
private Integer columns;
|
||||
/**
|
||||
* The width of the dialog buttons (default: 150, minimum: 1, maximum: 1024).
|
||||
*/
|
||||
@SerializedName("button_width")
|
||||
private Integer buttonWidth;
|
||||
|
||||
public DialogListDialog(@NonNull DialogBase base, Dialog... dialogs)
|
||||
{
|
||||
this( base, Arrays.asList( dialogs ), null, null, null );
|
||||
}
|
||||
|
||||
public DialogListDialog(@NonNull DialogBase base, List<Dialog> dialogs, ActionButton exitAction, Integer columns, Integer buttonWidth)
|
||||
{
|
||||
this.base = base;
|
||||
this.dialogs = dialogs;
|
||||
this.exitAction = exitAction;
|
||||
columns( columns );
|
||||
buttonWidth( buttonWidth );
|
||||
}
|
||||
|
||||
public DialogListDialog columns(Integer columns)
|
||||
{
|
||||
Preconditions.checkArgument( columns == null || columns > 0, "At least one column is required" );
|
||||
this.columns = columns;
|
||||
return this;
|
||||
}
|
||||
|
||||
public DialogListDialog buttonWidth(Integer buttonWidth)
|
||||
{
|
||||
Preconditions.checkArgument( buttonWidth == null || ( buttonWidth >= 1 && buttonWidth <= 1024 ), "buttonWidth must be between 1 and 1024" );
|
||||
this.buttonWidth = buttonWidth;
|
||||
return this;
|
||||
}
|
||||
}
|
@@ -0,0 +1,64 @@
|
||||
package net.md_5.bungee.api.dialog;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.NonNull;
|
||||
import lombok.ToString;
|
||||
import lombok.experimental.Accessors;
|
||||
import net.md_5.bungee.api.dialog.action.ActionButton;
|
||||
|
||||
/**
|
||||
* Represents a dialog with text a list of action buttons grouped into columns
|
||||
* and scrollable if necessary.
|
||||
*/
|
||||
@Data
|
||||
@ToString
|
||||
@EqualsAndHashCode
|
||||
@Accessors(fluent = true)
|
||||
public final class MultiActionDialog implements Dialog
|
||||
{
|
||||
|
||||
@NonNull
|
||||
@Accessors(fluent = false)
|
||||
private DialogBase base;
|
||||
/**
|
||||
* The action buttons in the dialog. At least one must be provided.
|
||||
*/
|
||||
@NonNull
|
||||
private List<ActionButton> actions;
|
||||
/**
|
||||
* The number of columns for the dialog buttons (default: 2).
|
||||
*/
|
||||
private Integer columns;
|
||||
/**
|
||||
* The {@link ActionButton} activated when the dialog is exited.
|
||||
*/
|
||||
@SerializedName("exit_action")
|
||||
private ActionButton exitAction;
|
||||
|
||||
public MultiActionDialog(@NonNull DialogBase base, @NonNull ActionButton... actions)
|
||||
{
|
||||
this( base, Arrays.asList( actions ), null, null );
|
||||
}
|
||||
|
||||
public MultiActionDialog(@NonNull DialogBase base, @NonNull List<ActionButton> actions, Integer columns, ActionButton exitAction)
|
||||
{
|
||||
Preconditions.checkArgument( !actions.isEmpty(), "At least one action must be provided" );
|
||||
|
||||
this.base = base;
|
||||
this.actions = actions;
|
||||
columns( columns );
|
||||
this.exitAction = exitAction;
|
||||
}
|
||||
|
||||
public MultiActionDialog columns(Integer columns)
|
||||
{
|
||||
Preconditions.checkArgument( columns == null || columns > 0, "At least one column is required" );
|
||||
this.columns = columns;
|
||||
return this;
|
||||
}
|
||||
}
|
@@ -0,0 +1,35 @@
|
||||
package net.md_5.bungee.api.dialog;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.NonNull;
|
||||
import lombok.ToString;
|
||||
import lombok.experimental.Accessors;
|
||||
import net.md_5.bungee.api.dialog.action.ActionButton;
|
||||
|
||||
/**
|
||||
* Represents a simple dialog with text and one action at the bottom (default:
|
||||
* "OK").
|
||||
*/
|
||||
@Data
|
||||
@ToString
|
||||
@EqualsAndHashCode
|
||||
@AllArgsConstructor
|
||||
@Accessors(fluent = true)
|
||||
public final class NoticeDialog implements Dialog
|
||||
{
|
||||
|
||||
@NonNull
|
||||
@Accessors(fluent = false)
|
||||
private DialogBase base;
|
||||
/**
|
||||
* The "OK" action button for the dialog.
|
||||
*/
|
||||
private ActionButton action;
|
||||
|
||||
public NoticeDialog(DialogBase base)
|
||||
{
|
||||
this( base, null );
|
||||
}
|
||||
}
|
@@ -0,0 +1,71 @@
|
||||
package net.md_5.bungee.api.dialog;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.NonNull;
|
||||
import lombok.ToString;
|
||||
import lombok.experimental.Accessors;
|
||||
import net.md_5.bungee.api.dialog.action.ActionButton;
|
||||
|
||||
/**
|
||||
* Represents a dialog which shows the links configured/sent from the server.
|
||||
*/
|
||||
@Data
|
||||
@ToString
|
||||
@EqualsAndHashCode
|
||||
@Accessors(fluent = true)
|
||||
public final class ServerLinksDialog implements Dialog
|
||||
{
|
||||
|
||||
@NonNull
|
||||
@Accessors(fluent = false)
|
||||
private DialogBase base;
|
||||
/**
|
||||
* The optional {@link ActionButton} for this dialog.
|
||||
*/
|
||||
@SerializedName("action")
|
||||
private ActionButton action;
|
||||
/**
|
||||
* The {@link ActionButton} activated when the dialog is exited.
|
||||
*/
|
||||
@SerializedName("exit_action")
|
||||
private ActionButton exitAction;
|
||||
/**
|
||||
* The number of columns for the dialog buttons (default: 2).
|
||||
*/
|
||||
private Integer columns;
|
||||
/**
|
||||
* The width of the dialog buttons (default: 150, minimum: 1, maximum: 1024).
|
||||
*/
|
||||
@SerializedName("button_width")
|
||||
private Integer buttonWidth;
|
||||
|
||||
public ServerLinksDialog(@NonNull DialogBase base)
|
||||
{
|
||||
this( base, null, null, null );
|
||||
}
|
||||
|
||||
public ServerLinksDialog(@NonNull DialogBase base, ActionButton action, Integer columns, Integer buttonWidth)
|
||||
{
|
||||
this.base = base;
|
||||
this.action = action;
|
||||
columns( columns );
|
||||
buttonWidth( buttonWidth );
|
||||
}
|
||||
|
||||
public ServerLinksDialog columns(Integer columns)
|
||||
{
|
||||
Preconditions.checkArgument( columns == null || columns > 0, "At least one column is required" );
|
||||
this.columns = columns;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ServerLinksDialog buttonWidth(Integer buttonWidth)
|
||||
{
|
||||
Preconditions.checkArgument( buttonWidth == null || ( buttonWidth >= 1 && buttonWidth <= 1024 ), "buttonWidth must be between 1 and 1024" );
|
||||
this.buttonWidth = buttonWidth;
|
||||
return this;
|
||||
}
|
||||
}
|
@@ -0,0 +1,6 @@
|
||||
package net.md_5.bungee.api.dialog.action;
|
||||
|
||||
public interface Action
|
||||
{
|
||||
|
||||
}
|
@@ -0,0 +1,54 @@
|
||||
package net.md_5.bungee.api.dialog.action;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import lombok.Data;
|
||||
import lombok.NonNull;
|
||||
import lombok.experimental.Accessors;
|
||||
import net.md_5.bungee.api.chat.BaseComponent;
|
||||
|
||||
/**
|
||||
* Represents a dialog action which will usually appear as a button.
|
||||
*/
|
||||
@Data
|
||||
@Accessors(fluent = true)
|
||||
public class ActionButton
|
||||
{
|
||||
|
||||
/**
|
||||
* The text label of the button, mandatory.
|
||||
*/
|
||||
@NonNull
|
||||
private BaseComponent label;
|
||||
/**
|
||||
* The hover tooltip of the button.
|
||||
*/
|
||||
private BaseComponent tooltip;
|
||||
/**
|
||||
* The width of the button (default: 150, minimum: 1, maximum: 1024).
|
||||
*/
|
||||
private Integer width;
|
||||
/**
|
||||
* The action to take.
|
||||
*/
|
||||
@NonNull
|
||||
private Action action;
|
||||
|
||||
public ActionButton(@NonNull BaseComponent label, BaseComponent tooltip, Integer width, @NonNull Action action)
|
||||
{
|
||||
this.label = label;
|
||||
this.tooltip = tooltip;
|
||||
setWidth( width );
|
||||
this.action = action;
|
||||
}
|
||||
|
||||
public ActionButton(@NonNull BaseComponent label, @NonNull Action action)
|
||||
{
|
||||
this( label, null, null, action );
|
||||
}
|
||||
|
||||
public void setWidth(Integer width)
|
||||
{
|
||||
Preconditions.checkArgument( width == null || ( width >= 1 && width <= 1024 ), "width must be between 1 and 1024" );
|
||||
this.width = width;
|
||||
}
|
||||
}
|
@@ -0,0 +1,27 @@
|
||||
package net.md_5.bungee.api.dialog.action;
|
||||
|
||||
import com.google.gson.JsonElement;
|
||||
import lombok.Data;
|
||||
import lombok.NonNull;
|
||||
import lombok.ToString;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
/**
|
||||
* Submits the dialog with the given ID and values as a payload.
|
||||
*/
|
||||
@Data
|
||||
@Accessors(fluent = true)
|
||||
@ToString(callSuper = true)
|
||||
public class CustomClickAction implements Action
|
||||
{
|
||||
|
||||
/**
|
||||
* The namespaced key of the submission.
|
||||
*/
|
||||
@NonNull
|
||||
private String id;
|
||||
/**
|
||||
* Fields to be added to the submission payload.
|
||||
*/
|
||||
private JsonElement additions;
|
||||
}
|
@@ -0,0 +1,25 @@
|
||||
package net.md_5.bungee.api.dialog.action;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.NonNull;
|
||||
import lombok.ToString;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
/**
|
||||
* Executes a command. If the command requires a permission
|
||||
* higher than 0, a confirmation dialog will be shown by the client.
|
||||
*/
|
||||
@Data
|
||||
@Accessors(fluent = true)
|
||||
@ToString(callSuper = true)
|
||||
public class RunCommandAction implements Action
|
||||
{
|
||||
|
||||
/**
|
||||
* The template to be applied, where variables of the form
|
||||
* <code>$(key)</code> will be replaced by their
|
||||
* {@link net.md_5.bungee.api.dialog.input.DialogInput#key} value.
|
||||
*/
|
||||
@NonNull
|
||||
private String template;
|
||||
}
|
@@ -0,0 +1,20 @@
|
||||
package net.md_5.bungee.api.dialog.action;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.NonNull;
|
||||
import lombok.ToString;
|
||||
import lombok.experimental.Accessors;
|
||||
import net.md_5.bungee.api.chat.ClickEvent;
|
||||
|
||||
/**
|
||||
* Represents a static dialog action.
|
||||
*/
|
||||
@Data
|
||||
@Accessors(fluent = true)
|
||||
@ToString(callSuper = true)
|
||||
public class StaticAction implements Action
|
||||
{
|
||||
|
||||
@NonNull
|
||||
private ClickEvent clickEvent;
|
||||
}
|
@@ -0,0 +1,4 @@
|
||||
/**
|
||||
* Contains the different actions/buttons for a {@link net.md_5.bungee.api.dialog.Dialog}.
|
||||
*/
|
||||
package net.md_5.bungee.api.dialog.action;
|
@@ -0,0 +1,20 @@
|
||||
package net.md_5.bungee.api.dialog.body;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.NonNull;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
|
||||
/**
|
||||
* Represents the body content of a {@link net.md_5.bungee.api.dialog.Dialog}.
|
||||
*/
|
||||
@Data
|
||||
public abstract class DialogBody
|
||||
{
|
||||
|
||||
/**
|
||||
* The internal body type.
|
||||
*/
|
||||
@NonNull
|
||||
@ApiStatus.Internal
|
||||
private final String type;
|
||||
}
|
@@ -0,0 +1,49 @@
|
||||
package net.md_5.bungee.api.dialog.body;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.NonNull;
|
||||
import lombok.ToString;
|
||||
import lombok.experimental.Accessors;
|
||||
import net.md_5.bungee.api.chat.BaseComponent;
|
||||
|
||||
/**
|
||||
* Represents a dialog body which consists of text constrained to a certain
|
||||
* width.
|
||||
*/
|
||||
@Data
|
||||
@Accessors(fluent = true)
|
||||
@ToString(callSuper = true)
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class PlainMessageBody extends DialogBody
|
||||
{
|
||||
|
||||
/**
|
||||
* The text body.
|
||||
*/
|
||||
@NonNull
|
||||
private BaseComponent contents;
|
||||
/**
|
||||
* The maximum width (default: 200, minimum: 1, maximum: 1024).
|
||||
*/
|
||||
private Integer width;
|
||||
|
||||
public PlainMessageBody(@NonNull BaseComponent contents)
|
||||
{
|
||||
this( contents, null );
|
||||
}
|
||||
|
||||
public PlainMessageBody(@NonNull BaseComponent contents, Integer width)
|
||||
{
|
||||
super( "minecraft:plain_message" );
|
||||
this.contents = contents;
|
||||
width( width );
|
||||
}
|
||||
|
||||
public void width(Integer width)
|
||||
{
|
||||
Preconditions.checkArgument( width == null || ( width >= 1 && width <= 1024 ), "width must be between 1 and 1024" );
|
||||
this.width = width;
|
||||
}
|
||||
}
|
@@ -0,0 +1,5 @@
|
||||
/**
|
||||
* Contains the different {@link net.md_5.bungee.api.dialog.Dialog} body content
|
||||
* types.
|
||||
*/
|
||||
package net.md_5.bungee.api.dialog.body;
|
@@ -0,0 +1,42 @@
|
||||
package net.md_5.bungee.api.dialog.chat;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import net.md_5.bungee.api.chat.ClickEvent;
|
||||
import net.md_5.bungee.api.dialog.Dialog;
|
||||
|
||||
/**
|
||||
* Click event which displays either a pre-existing dialog by key or a custom
|
||||
* dialog.
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = false)
|
||||
public class ShowDialogClickEvent extends ClickEvent
|
||||
{
|
||||
|
||||
/**
|
||||
* Key for a pre-existing dialog to show.
|
||||
*/
|
||||
private String reference;
|
||||
/**
|
||||
* Dialog to show.
|
||||
*/
|
||||
private Dialog dialog;
|
||||
|
||||
public ShowDialogClickEvent(String reference)
|
||||
{
|
||||
this( reference, null );
|
||||
}
|
||||
|
||||
public ShowDialogClickEvent(Dialog dialog)
|
||||
{
|
||||
this( null, dialog );
|
||||
}
|
||||
|
||||
private ShowDialogClickEvent(String reference, Dialog dialog)
|
||||
{
|
||||
super( Action.SHOW_DIALOG, null );
|
||||
this.reference = reference;
|
||||
this.dialog = dialog;
|
||||
}
|
||||
}
|
@@ -0,0 +1,4 @@
|
||||
/**
|
||||
* Contains dialog extensions to the chat API.
|
||||
*/
|
||||
package net.md_5.bungee.api.dialog.chat;
|
@@ -0,0 +1,54 @@
|
||||
package net.md_5.bungee.api.dialog.input;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.NonNull;
|
||||
import lombok.ToString;
|
||||
import lombok.experimental.Accessors;
|
||||
import net.md_5.bungee.api.chat.BaseComponent;
|
||||
|
||||
/**
|
||||
* Represents a checkbox input control.
|
||||
*/
|
||||
@Data
|
||||
@Accessors(fluent = true)
|
||||
@ToString(callSuper = true)
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class BooleanInput extends DialogInput
|
||||
{
|
||||
|
||||
/**
|
||||
* The input label.
|
||||
*/
|
||||
@NonNull
|
||||
private BaseComponent label;
|
||||
/**
|
||||
* The initial value (default: false/unchecked).
|
||||
*/
|
||||
private Boolean initial;
|
||||
/**
|
||||
* The string value to be submitted when true/checked (default: "true").
|
||||
*/
|
||||
@SerializedName("on_true")
|
||||
private String onTrue;
|
||||
/**
|
||||
* The string value to be submitted when false/unchecked (default: "false").
|
||||
*/
|
||||
@SerializedName("on_false")
|
||||
private String onFalse;
|
||||
|
||||
public BooleanInput(@NonNull String key, @NonNull BaseComponent label)
|
||||
{
|
||||
this( key, label, null, "true", "false" );
|
||||
}
|
||||
|
||||
public BooleanInput(@NonNull String key, @NonNull BaseComponent label, Boolean initial, String onTrue, String onFalse)
|
||||
{
|
||||
super( "minecraft:boolean", key );
|
||||
this.label = label;
|
||||
this.initial = initial;
|
||||
this.onTrue = onTrue;
|
||||
this.onFalse = onFalse;
|
||||
}
|
||||
}
|
@@ -0,0 +1,29 @@
|
||||
package net.md_5.bungee.api.dialog.input;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.NonNull;
|
||||
import lombok.experimental.Accessors;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
|
||||
/**
|
||||
* Represents a type of input which may be displayed/submitted with a form
|
||||
* dialog.
|
||||
*/
|
||||
@Data
|
||||
@Accessors(fluent = true)
|
||||
public class DialogInput
|
||||
{
|
||||
|
||||
/**
|
||||
* The internal input type.
|
||||
*/
|
||||
@NonNull
|
||||
@ApiStatus.Internal
|
||||
private final String type;
|
||||
/**
|
||||
* The key corresponding to this input and associated with the value
|
||||
* submitted.
|
||||
*/
|
||||
@NonNull
|
||||
private final String key;
|
||||
}
|
@@ -0,0 +1,39 @@
|
||||
package net.md_5.bungee.api.dialog.input;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NonNull;
|
||||
import lombok.experimental.Accessors;
|
||||
import net.md_5.bungee.api.chat.BaseComponent;
|
||||
|
||||
/**
|
||||
* Represents an option choice which may form part of a
|
||||
* {@link SingleOptionInput}.
|
||||
*/
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@Accessors(fluent = true)
|
||||
public class InputOption
|
||||
{
|
||||
|
||||
/**
|
||||
* The string value associated with this option, to be submitted when
|
||||
* selected.
|
||||
*/
|
||||
@NonNull
|
||||
private String id;
|
||||
/**
|
||||
* The text to display for this option.
|
||||
*/
|
||||
private BaseComponent display;
|
||||
/**
|
||||
* Whether this option is the one initially selected. Only one option may
|
||||
* have this value as true (default: first option).
|
||||
*/
|
||||
private Boolean initial;
|
||||
|
||||
public InputOption(@NonNull String id)
|
||||
{
|
||||
this( id, null, null );
|
||||
}
|
||||
}
|
@@ -0,0 +1,103 @@
|
||||
package net.md_5.bungee.api.dialog.input;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.NonNull;
|
||||
import lombok.ToString;
|
||||
import lombok.experimental.Accessors;
|
||||
import net.md_5.bungee.api.chat.BaseComponent;
|
||||
|
||||
/**
|
||||
* Represents a number slider input.
|
||||
*/
|
||||
@Data
|
||||
@Accessors(fluent = true)
|
||||
@ToString(callSuper = true)
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class NumberRangeInput extends DialogInput
|
||||
{
|
||||
|
||||
/**
|
||||
* The width of the input (default: 200, minimum: 1, maximum: 1024).
|
||||
*/
|
||||
private Integer width;
|
||||
/**
|
||||
* The label of the slider.
|
||||
*/
|
||||
@NonNull
|
||||
private BaseComponent label;
|
||||
/**
|
||||
* A translate key used to display the label value (default:
|
||||
* options.generic_value).
|
||||
*/
|
||||
private String labelFormat;
|
||||
/**
|
||||
* The start position of the slider (leftmost position).
|
||||
*/
|
||||
private float start;
|
||||
/**
|
||||
* The end position of the slider (rightmost position).
|
||||
*/
|
||||
private float end;
|
||||
/**
|
||||
* The steps in which the input will be increased or decreased, or null if
|
||||
* no specific steps.
|
||||
*/
|
||||
private Float step;
|
||||
/**
|
||||
* The initial value of number input, or null to fall back to the middle.
|
||||
*/
|
||||
private Float initial;
|
||||
|
||||
public NumberRangeInput(@NonNull String key, @NonNull BaseComponent label, float start, float end)
|
||||
{
|
||||
this( key, null, label, "options.generic_value", start, end, null, null );
|
||||
}
|
||||
|
||||
public NumberRangeInput(@NonNull String key, @NonNull BaseComponent label, float start, float end, Float step)
|
||||
{
|
||||
this( key, null, label, "options.generic_value", start, end, step, null );
|
||||
}
|
||||
|
||||
public NumberRangeInput(@NonNull String key, @NonNull BaseComponent label, float start, float end, Float step, Float initial)
|
||||
{
|
||||
this( key, null, label, "options.generic_value", start, end, step, initial );
|
||||
}
|
||||
|
||||
public NumberRangeInput(@NonNull String key, Integer width, @NonNull BaseComponent label, String labelFormat, float start, float end, Float step, Float initial)
|
||||
{
|
||||
super( "minecraft:number_range", key );
|
||||
width( width );
|
||||
this.label = label;
|
||||
this.labelFormat = labelFormat;
|
||||
this.start = start;
|
||||
this.end = end;
|
||||
step( step );
|
||||
initial( initial );
|
||||
}
|
||||
|
||||
public NumberRangeInput width(Integer width)
|
||||
{
|
||||
Preconditions.checkArgument( width == null || ( width >= 1 && width <= 1024 ), "with must be between 1 and 1024" );
|
||||
this.width = width;
|
||||
return this;
|
||||
}
|
||||
|
||||
public NumberRangeInput step(Float step)
|
||||
{
|
||||
Preconditions.checkArgument( step == null || step > 0, "step must be null or greater than zero" );
|
||||
this.step = step;
|
||||
return this;
|
||||
}
|
||||
|
||||
public NumberRangeInput initial(Float initial)
|
||||
{
|
||||
// we need to calculate if the initial value is between start and end, regardless of the order
|
||||
float min = Math.min( start, end );
|
||||
float max = Math.max( start, end );
|
||||
Preconditions.checkArgument( initial == null || ( initial >= min && initial <= max ), "initial must be null or between start and end" );
|
||||
this.initial = initial;
|
||||
return this;
|
||||
}
|
||||
}
|
@@ -0,0 +1,66 @@
|
||||
package net.md_5.bungee.api.dialog.input;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.NonNull;
|
||||
import lombok.ToString;
|
||||
import lombok.experimental.Accessors;
|
||||
import net.md_5.bungee.api.chat.BaseComponent;
|
||||
|
||||
/**
|
||||
* Represents a single option (dropdown) input.
|
||||
*/
|
||||
@Data
|
||||
@Accessors(fluent = true)
|
||||
@ToString(callSuper = true)
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class SingleOptionInput extends DialogInput
|
||||
{
|
||||
|
||||
/**
|
||||
* The width of the input (default: 200, minimum: 1, maximum: 1024).
|
||||
*/
|
||||
private Integer width;
|
||||
/**
|
||||
* The input label.
|
||||
*/
|
||||
@NonNull
|
||||
private BaseComponent label;
|
||||
/**
|
||||
* Whether the label is visible (default: true).
|
||||
*/
|
||||
@SerializedName("label_visible")
|
||||
private Boolean labelVisible;
|
||||
/**
|
||||
* The non-empty list of options to be selected from.
|
||||
*/
|
||||
@NonNull
|
||||
private List<InputOption> options;
|
||||
|
||||
public SingleOptionInput(@NonNull String key, @NonNull BaseComponent label, @NonNull InputOption... options)
|
||||
{
|
||||
this( key, null, label, null, Arrays.asList( options ) );
|
||||
}
|
||||
|
||||
public SingleOptionInput(@NonNull String key, Integer width, @NonNull BaseComponent label, Boolean labelVisible, @NonNull List<InputOption> options)
|
||||
{
|
||||
super( "minecraft:single_option", key );
|
||||
Preconditions.checkArgument( !options.isEmpty(), "At least one option must be provided" );
|
||||
|
||||
width( width );
|
||||
this.label = label;
|
||||
this.labelVisible = labelVisible;
|
||||
this.options = options;
|
||||
}
|
||||
|
||||
public SingleOptionInput width(Integer width)
|
||||
{
|
||||
Preconditions.checkArgument( width == null || ( width >= 1 && width <= 1024 ), "width must be between 1 and 1024" );
|
||||
this.width = width;
|
||||
return this;
|
||||
}
|
||||
}
|
@@ -0,0 +1,110 @@
|
||||
package net.md_5.bungee.api.dialog.input;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.NonNull;
|
||||
import lombok.ToString;
|
||||
import lombok.experimental.Accessors;
|
||||
import net.md_5.bungee.api.chat.BaseComponent;
|
||||
|
||||
/**
|
||||
* Represents a textbox input.
|
||||
*/
|
||||
@Data
|
||||
@Accessors(fluent = true)
|
||||
@ToString(callSuper = true)
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class TextInput extends DialogInput
|
||||
{
|
||||
|
||||
/**
|
||||
* The width of this text input (default: 200, minimum: 1, maximum: 1024).
|
||||
*/
|
||||
private Integer width;
|
||||
/**
|
||||
* The label of this text input.
|
||||
*/
|
||||
@NonNull
|
||||
private BaseComponent label;
|
||||
/**
|
||||
* The visibility of this text input's label.
|
||||
*/
|
||||
@SerializedName("label_visible")
|
||||
private Boolean labelVisible;
|
||||
/**
|
||||
* The initial value of this text input.
|
||||
*/
|
||||
private String initial;
|
||||
/**
|
||||
* The maximum length of the input (default: 32).
|
||||
*/
|
||||
@SerializedName("max_length")
|
||||
private Integer maxLength;
|
||||
/**
|
||||
* If present, allows users to input multiple lines.
|
||||
*/
|
||||
private Multiline multiline;
|
||||
|
||||
public TextInput(@NonNull String key, @NonNull BaseComponent label)
|
||||
{
|
||||
this( key, null, label, null, null, null, null );
|
||||
}
|
||||
|
||||
public TextInput(@NonNull String key, Integer width, @NonNull BaseComponent label, Boolean labelVisible, String initial, Integer maxLength)
|
||||
{
|
||||
this( key, width, label, labelVisible, initial, maxLength, null );
|
||||
}
|
||||
|
||||
public TextInput(@NonNull String key, Integer width, @NonNull BaseComponent label, Boolean labelVisible, String initial, Integer maxLength, Multiline multiline)
|
||||
{
|
||||
super( "minecraft:text", key );
|
||||
width( width );
|
||||
this.label = label;
|
||||
this.labelVisible = labelVisible;
|
||||
this.initial = initial;
|
||||
this.maxLength = maxLength;
|
||||
this.multiline = multiline;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configuration data for a multiline input.
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@Accessors(fluent = true)
|
||||
public static class Multiline
|
||||
{
|
||||
|
||||
/**
|
||||
* The maximum length of input, or null to disable any limits.
|
||||
*/
|
||||
@SerializedName("max_lines")
|
||||
private Integer maxLines;
|
||||
/**
|
||||
* The height of this input (default: 32, minimum: 1, maximum: 512).
|
||||
*/
|
||||
private Integer height;
|
||||
|
||||
public Multiline(Integer maxLines, Integer height)
|
||||
{
|
||||
height( height ).maxLines( maxLines );
|
||||
}
|
||||
|
||||
public Multiline height(Integer height)
|
||||
{
|
||||
Preconditions.checkArgument( height == null || height >= 1 && height <= 512, "height must null or be between 1 and 512" );
|
||||
this.height = height;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
public TextInput width(Integer width)
|
||||
{
|
||||
Preconditions.checkArgument( width == null || ( width >= 1 && width <= 1024 ), "width must be between 1 and 1024" );
|
||||
this.width = width;
|
||||
return this;
|
||||
}
|
||||
}
|
@@ -0,0 +1,4 @@
|
||||
/**
|
||||
* Represents the various input controls which may be present on form dialogs.
|
||||
*/
|
||||
package net.md_5.bungee.api.dialog.input;
|
@@ -0,0 +1,4 @@
|
||||
/**
|
||||
* Contains the core classes for the display of a {@link net.md_5.bungee.api.dialog.Dialog}.
|
||||
*/
|
||||
package net.md_5.bungee.api.dialog;
|
@@ -6,13 +6,13 @@
|
||||
<parent>
|
||||
<groupId>net.md-5</groupId>
|
||||
<artifactId>bungeecord-parent</artifactId>
|
||||
<version>1.21-R0.1-SNAPSHOT</version>
|
||||
<version>1.21-R0.4-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
<groupId>net.md-5</groupId>
|
||||
<artifactId>bungeecord-event</artifactId>
|
||||
<version>1.21-R0.1-SNAPSHOT</version>
|
||||
<version>1.21-R0.4-SNAPSHOT</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>BungeeCord-Event</name>
|
||||
|
@@ -6,13 +6,13 @@
|
||||
<parent>
|
||||
<groupId>net.md-5</groupId>
|
||||
<artifactId>bungeecord-parent</artifactId>
|
||||
<version>1.21-R0.1-SNAPSHOT</version>
|
||||
<version>1.21-R0.4-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
<groupId>net.md-5</groupId>
|
||||
<artifactId>bungeecord-log</artifactId>
|
||||
<version>1.21-R0.1-SNAPSHOT</version>
|
||||
<version>1.21-R0.4-SNAPSHOT</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>BungeeCord-Log</name>
|
||||
@@ -20,9 +20,9 @@
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>jline</groupId>
|
||||
<groupId>org.jline</groupId>
|
||||
<artifactId>jline</artifactId>
|
||||
<version>2.12.1</version>
|
||||
<version>3.30.4</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
|
@@ -1,26 +1,18 @@
|
||||
package net.md_5.bungee.log;
|
||||
|
||||
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
|
||||
import java.io.IOException;
|
||||
import java.util.logging.FileHandler;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.LogRecord;
|
||||
import java.util.logging.Logger;
|
||||
import jline.console.ConsoleReader;
|
||||
import org.jline.reader.LineReader;
|
||||
|
||||
public class BungeeLogger extends Logger
|
||||
{
|
||||
|
||||
private final LogDispatcher dispatcher = new LogDispatcher( this );
|
||||
|
||||
// CHECKSTYLE:OFF
|
||||
@SuppressWarnings(
|
||||
{
|
||||
"CallToPrintStackTrace", "CallToThreadStartDuringObjectConstruction"
|
||||
})
|
||||
// CHECKSTYLE:ON
|
||||
@SuppressFBWarnings("SC_START_IN_CTOR")
|
||||
public BungeeLogger(String loggerName, String filePattern, ConsoleReader reader)
|
||||
public BungeeLogger(String loggerName, String filePattern, LineReader reader)
|
||||
{
|
||||
super( loggerName, null );
|
||||
setLevel( Level.ALL );
|
||||
|
@@ -1,15 +1,15 @@
|
||||
package net.md_5.bungee.log;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.logging.Handler;
|
||||
import java.util.logging.LogRecord;
|
||||
import java.util.regex.Pattern;
|
||||
import jline.console.ConsoleReader;
|
||||
import lombok.Data;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import net.md_5.bungee.api.ChatColor;
|
||||
import org.fusesource.jansi.Ansi;
|
||||
import org.fusesource.jansi.Ansi.Erase;
|
||||
import org.jline.jansi.Ansi;
|
||||
import org.jline.reader.LineReader;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public class ColouredWriter extends Handler
|
||||
{
|
||||
|
||||
@@ -52,12 +52,7 @@ public class ColouredWriter extends Handler
|
||||
compile( ChatColor.RESET, Ansi.ansi().a( Ansi.Attribute.RESET ).toString() ),
|
||||
};
|
||||
//
|
||||
private final ConsoleReader console;
|
||||
|
||||
public ColouredWriter(ConsoleReader console)
|
||||
{
|
||||
this.console = console;
|
||||
}
|
||||
private final LineReader console;
|
||||
|
||||
public void print(String s)
|
||||
{
|
||||
@@ -65,14 +60,7 @@ public class ColouredWriter extends Handler
|
||||
{
|
||||
s = replacement.pattern.matcher( s ).replaceAll( replacement.replacement );
|
||||
}
|
||||
try
|
||||
{
|
||||
console.print( Ansi.ansi().eraseLine( Erase.ALL ).toString() + ConsoleReader.RESET_LINE + s + Ansi.ansi().reset().toString() );
|
||||
console.drawLine();
|
||||
console.flush();
|
||||
} catch ( IOException ex )
|
||||
{
|
||||
}
|
||||
console.printAbove( s + Ansi.ansi().reset().toString() );
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@@ -18,7 +18,6 @@ public class ConciseFormatter extends Formatter
|
||||
private final boolean coloured;
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("ThrowableResultIgnored")
|
||||
public String format(LogRecord record)
|
||||
{
|
||||
StringBuilder formatted = new StringBuilder();
|
||||
|
@@ -6,13 +6,13 @@
|
||||
<parent>
|
||||
<groupId>net.md-5</groupId>
|
||||
<artifactId>bungeecord-module</artifactId>
|
||||
<version>1.21-R0.1-SNAPSHOT</version>
|
||||
<version>1.21-R0.4-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
<groupId>net.md-5</groupId>
|
||||
<artifactId>bungeecord-module-cmd-alert</artifactId>
|
||||
<version>1.21-R0.1-SNAPSHOT</version>
|
||||
<version>1.21-R0.4-SNAPSHOT</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>cmd_alert</name>
|
||||
|
@@ -15,7 +15,7 @@ public class CommandAlertRaw extends Command
|
||||
|
||||
public CommandAlertRaw()
|
||||
{
|
||||
super( "alertraw", "bungeecord.command.alert" );
|
||||
super( "alertraw", "bungeecord.command.alertraw" );
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@@ -6,13 +6,13 @@
|
||||
<parent>
|
||||
<groupId>net.md-5</groupId>
|
||||
<artifactId>bungeecord-module</artifactId>
|
||||
<version>1.21-R0.1-SNAPSHOT</version>
|
||||
<version>1.21-R0.4-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
<groupId>net.md-5</groupId>
|
||||
<artifactId>bungeecord-module-cmd-find</artifactId>
|
||||
<version>1.21-R0.1-SNAPSHOT</version>
|
||||
<version>1.21-R0.4-SNAPSHOT</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>cmd_find</name>
|
||||
|
@@ -6,13 +6,13 @@
|
||||
<parent>
|
||||
<groupId>net.md-5</groupId>
|
||||
<artifactId>bungeecord-module</artifactId>
|
||||
<version>1.21-R0.1-SNAPSHOT</version>
|
||||
<version>1.21-R0.4-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
<groupId>net.md-5</groupId>
|
||||
<artifactId>bungeecord-module-cmd-kick</artifactId>
|
||||
<version>1.21-R0.1-SNAPSHOT</version>
|
||||
<version>1.21-R0.4-SNAPSHOT</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>cmd_kick</name>
|
||||
|
@@ -6,13 +6,13 @@
|
||||
<parent>
|
||||
<groupId>net.md-5</groupId>
|
||||
<artifactId>bungeecord-module</artifactId>
|
||||
<version>1.21-R0.1-SNAPSHOT</version>
|
||||
<version>1.21-R0.4-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
<groupId>net.md-5</groupId>
|
||||
<artifactId>bungeecord-module-cmd-list</artifactId>
|
||||
<version>1.21-R0.1-SNAPSHOT</version>
|
||||
<version>1.21-R0.4-SNAPSHOT</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>cmd_list</name>
|
||||
|
@@ -6,13 +6,13 @@
|
||||
<parent>
|
||||
<groupId>net.md-5</groupId>
|
||||
<artifactId>bungeecord-module</artifactId>
|
||||
<version>1.21-R0.1-SNAPSHOT</version>
|
||||
<version>1.21-R0.4-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
<groupId>net.md-5</groupId>
|
||||
<artifactId>bungeecord-module-cmd-send</artifactId>
|
||||
<version>1.21-R0.1-SNAPSHOT</version>
|
||||
<version>1.21-R0.4-SNAPSHOT</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>cmd_send</name>
|
||||
|
@@ -6,13 +6,13 @@
|
||||
<parent>
|
||||
<groupId>net.md-5</groupId>
|
||||
<artifactId>bungeecord-module</artifactId>
|
||||
<version>1.21-R0.1-SNAPSHOT</version>
|
||||
<version>1.21-R0.4-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
<groupId>net.md-5</groupId>
|
||||
<artifactId>bungeecord-module-cmd-server</artifactId>
|
||||
<version>1.21-R0.1-SNAPSHOT</version>
|
||||
<version>1.21-R0.4-SNAPSHOT</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>cmd_server</name>
|
||||
|
@@ -6,13 +6,13 @@
|
||||
<parent>
|
||||
<groupId>net.md-5</groupId>
|
||||
<artifactId>bungeecord-parent</artifactId>
|
||||
<version>1.21-R0.1-SNAPSHOT</version>
|
||||
<version>1.21-R0.4-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
<groupId>net.md-5</groupId>
|
||||
<artifactId>bungeecord-module</artifactId>
|
||||
<version>1.21-R0.1-SNAPSHOT</version>
|
||||
<version>1.21-R0.4-SNAPSHOT</version>
|
||||
<packaging>pom</packaging>
|
||||
|
||||
<name>BungeeCord Modules</name>
|
||||
|
@@ -6,13 +6,13 @@
|
||||
<parent>
|
||||
<groupId>net.md-5</groupId>
|
||||
<artifactId>bungeecord-module</artifactId>
|
||||
<version>1.21-R0.1-SNAPSHOT</version>
|
||||
<version>1.21-R0.4-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
<groupId>net.md-5</groupId>
|
||||
<artifactId>bungeecord-module-reconnect-yaml</artifactId>
|
||||
<version>1.21-R0.1-SNAPSHOT</version>
|
||||
<version>1.21-R0.4-SNAPSHOT</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>reconnect_yaml</name>
|
||||
|
29
native/compile-native-arm.sh
Executable file
29
native/compile-native-arm.sh
Executable file
@@ -0,0 +1,29 @@
|
||||
#!/bin/sh
|
||||
|
||||
set -eu
|
||||
|
||||
CWD=$(pwd)
|
||||
|
||||
echo "Compiling mbedtls"
|
||||
(cd mbedtls && CFLAGS="-fPIC -I$CWD/src/main/c -DMBEDTLS_USER_CONFIG_FILE='<mbedtls_custom_config.h>'" make CC=aarch64-linux-gnu-gcc AR=aarch64-linux-gnu-ar no_test)
|
||||
|
||||
echo "Compiling zlib"
|
||||
(cd zlib && CFLAGS="-fPIC -DNO_GZIP" CC=aarch64-linux-gnu-gcc CHOST=arm64 ./configure --target="aarch64" --static && make CFLAGS="-fPIC -march=armv8-a+crc" CC=aarch64-linux-gnu-gcc AR=aarch64-linux-gnu-ar)
|
||||
|
||||
CC="aarch64-linux-gnu-gcc"
|
||||
CFLAGS="-c -fPIC -O3 -Wall -Werror -I$JAVA_HOME/include/ -I$JAVA_HOME/include/linux/"
|
||||
LDFLAGS="-shared"
|
||||
|
||||
echo "Compiling bungee"
|
||||
$CC $CFLAGS -o shared.o src/main/c/shared.c
|
||||
$CC $CFLAGS -Imbedtls/include -o NativeCipherImpl.o src/main/c/NativeCipherImpl.c
|
||||
$CC $CFLAGS -Izlib -o NativeCompressImpl.o src/main/c/NativeCompressImpl.c
|
||||
|
||||
echo "Linking native-cipher-arm.so"
|
||||
$CC $LDFLAGS -o src/main/resources/native-cipher-arm.so shared.o NativeCipherImpl.o mbedtls/library/libmbedcrypto.a
|
||||
|
||||
echo "Linking native-compress-arm.so"
|
||||
$CC $LDFLAGS -o src/main/resources/native-compress-arm.so shared.o NativeCompressImpl.o zlib/libz.a
|
||||
|
||||
echo "Cleaning up"
|
||||
rm shared.o NativeCipherImpl.o NativeCompressImpl.o
|
@@ -2,11 +2,13 @@
|
||||
|
||||
set -eu
|
||||
|
||||
CWD=$(pwd)
|
||||
|
||||
echo "Compiling mbedtls"
|
||||
(cd mbedtls && make no_test)
|
||||
(cd mbedtls && CFLAGS="-fPIC -I$CWD/src/main/c -DMBEDTLS_USER_CONFIG_FILE='<mbedtls_custom_config.h>'" make no_test)
|
||||
|
||||
echo "Compiling zlib"
|
||||
(cd zlib && CFLAGS=-fPIC ./configure --static && make)
|
||||
(cd zlib && CFLAGS="-fPIC -DNO_GZIP" ./configure --static && make)
|
||||
|
||||
CC="gcc"
|
||||
CFLAGS="-c -fPIC -O3 -Wall -Werror -I$JAVA_HOME/include/ -I$JAVA_HOME/include/linux/"
|
||||
|
@@ -6,13 +6,13 @@
|
||||
<parent>
|
||||
<groupId>net.md-5</groupId>
|
||||
<artifactId>bungeecord-parent</artifactId>
|
||||
<version>1.21-R0.1-SNAPSHOT</version>
|
||||
<version>1.21-R0.4-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
<groupId>net.md-5</groupId>
|
||||
<artifactId>bungeecord-native</artifactId>
|
||||
<version>1.21-R0.1-SNAPSHOT</version>
|
||||
<version>1.21-R0.4-SNAPSHOT</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>BungeeCord-Native</name>
|
||||
|
@@ -5,11 +5,15 @@
|
||||
#include "shared.h"
|
||||
#include "net_md_5_bungee_jni_cipher_NativeCipherImpl.h"
|
||||
|
||||
// Hack to keep the compiler from optimizing the memset away
|
||||
static void *(*const volatile memset_func)(void *, int, size_t) = memset;
|
||||
|
||||
typedef unsigned char byte;
|
||||
|
||||
typedef struct crypto_context {
|
||||
int mode;
|
||||
mbedtls_aes_context cipher;
|
||||
int keyLen;
|
||||
byte key[];
|
||||
} crypto_context;
|
||||
|
||||
@@ -22,6 +26,7 @@ jlong JNICALL Java_net_md_15_bungee_jni_cipher_NativeCipherImpl_init(JNIEnv* env
|
||||
return 0;
|
||||
}
|
||||
|
||||
crypto->keyLen = (int) keyLen;
|
||||
(*env)->GetByteArrayRegion(env, key, 0, keyLen, (jbyte*) &crypto->key);
|
||||
|
||||
mbedtls_aes_init(&crypto->cipher);
|
||||
@@ -36,6 +41,7 @@ void Java_net_md_15_bungee_jni_cipher_NativeCipherImpl_free(JNIEnv* env, jobject
|
||||
crypto_context *crypto = (crypto_context*) ctx;
|
||||
|
||||
mbedtls_aes_free(&crypto->cipher);
|
||||
memset_func(crypto->key, 0, (size_t) crypto->keyLen);
|
||||
free(crypto);
|
||||
}
|
||||
|
||||
|
@@ -3,6 +3,9 @@
|
||||
|
||||
#include <zlib.h>
|
||||
#include "shared.h"
|
||||
#if !defined(__aarch64__)
|
||||
#include "cpuid_helper.h"
|
||||
#endif
|
||||
#include "net_md_5_bungee_jni_zlib_NativeCompressImpl.h"
|
||||
|
||||
typedef unsigned char byte;
|
||||
@@ -26,6 +29,14 @@ jint throwException(JNIEnv *env, const char* message, int err) {
|
||||
return (*env)->Throw(env, throwable);
|
||||
}
|
||||
|
||||
JNIEXPORT jboolean JNICALL Java_net_md_15_bungee_jni_zlib_NativeCompressImpl_checkSupported(JNIEnv* env, jobject obj) {
|
||||
#if !defined(__aarch64__)
|
||||
return (jboolean) checkCompressionNativesSupport();
|
||||
#else
|
||||
return JNI_TRUE;
|
||||
#endif
|
||||
}
|
||||
|
||||
void JNICALL Java_net_md_15_bungee_jni_zlib_NativeCompressImpl_reset(JNIEnv* env, jobject obj, jlong ctx, jboolean compress) {
|
||||
z_stream* stream = (z_stream*) ctx;
|
||||
int ret = (compress) ? deflateReset(stream) : inflateReset(stream);
|
||||
|
19
native/src/main/c/cpuid_helper.h
Normal file
19
native/src/main/c/cpuid_helper.h
Normal file
@@ -0,0 +1,19 @@
|
||||
// Header to check for SSE 2, SSSE 3, and SSE 4.2 support in compression natives
|
||||
// GCC only!
|
||||
|
||||
#ifndef _INCLUDE_CPUID_HELPER_H
|
||||
#define _INCLUDE_CPUID_HELPER_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <cpuid.h>
|
||||
|
||||
static inline bool checkCompressionNativesSupport() {
|
||||
unsigned int eax, ebx, ecx, edx;
|
||||
if(__get_cpuid(1, &eax, &ebx, &ecx, &edx)) {
|
||||
return (edx & bit_SSE2) != 0 && (ecx & bit_SSSE3) != 0 && (ecx & bit_SSE4_2) != 0;
|
||||
}else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
#endif // _INCLUDE_CPUID_HELPER_H
|
31
native/src/main/c/mbedtls_custom_config.h
Normal file
31
native/src/main/c/mbedtls_custom_config.h
Normal file
@@ -0,0 +1,31 @@
|
||||
|
||||
// This is a hack to deal with a glitch that happens when mbedtls is compiled against glibc
|
||||
// but then run on a linux distro that uses musl libc. This implementation of the zeroize
|
||||
// is compatible with both glibc and musl without requiring the library to be recompiled.
|
||||
|
||||
// I checked with a disassembler and for BungeeCord's usage of the library, implementing
|
||||
// this function as a static function only resulted in 2 different subroutines referencing
|
||||
// different versions of memset_func, so we might as well keep things simple and use a
|
||||
// static function here instead of requiring the mbedtls makefile to be modified to add
|
||||
// additional source files.
|
||||
|
||||
#ifndef _INCLUDE_MBEDTLS_CUSTOM_CONFIG_H
|
||||
#define _INCLUDE_MBEDTLS_CUSTOM_CONFIG_H
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#define MBEDTLS_PLATFORM_ZEROIZE_ALT
|
||||
|
||||
#define mbedtls_platform_zeroize mbedtls_platform_zeroize_impl
|
||||
|
||||
// hack to prevent compilers from optimizing the memset away
|
||||
static void *(*const volatile memset_func)(void *, int, size_t) = memset;
|
||||
|
||||
static void mbedtls_platform_zeroize_impl(void *buf, size_t len) {
|
||||
if (len > 0) {
|
||||
memset_func(buf, 0, len);
|
||||
}
|
||||
}
|
||||
|
||||
#endif // _INCLUDE_MBEDTLS_CUSTOM_CONFIG_H
|
||||
|
@@ -15,6 +15,14 @@ extern "C" {
|
||||
JNIEXPORT void JNICALL Java_net_md_15_bungee_jni_zlib_NativeCompressImpl_initFields
|
||||
(JNIEnv *, jclass);
|
||||
|
||||
/*
|
||||
* Class: net_md_5_bungee_jni_zlib_NativeCompressImpl
|
||||
* Method: checkSupported
|
||||
* Signature: ()Z
|
||||
*/
|
||||
JNIEXPORT jboolean JNICALL Java_net_md_15_bungee_jni_zlib_NativeCompressImpl_checkSupported
|
||||
(JNIEnv *, jobject);
|
||||
|
||||
/*
|
||||
* Class: net_md_5_bungee_jni_zlib_NativeCompressImpl
|
||||
* Method: end
|
||||
|
@@ -1,6 +1,8 @@
|
||||
package net.md_5.bungee.jni;
|
||||
|
||||
import com.google.common.io.ByteStreams;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
@@ -15,14 +17,23 @@ public final class NativeCode<T>
|
||||
private final String name;
|
||||
private final Supplier<? extends T> javaImpl;
|
||||
private final Supplier<? extends T> nativeImpl;
|
||||
private final boolean enableNativeFlag;
|
||||
private final boolean extendedSupportCheck;
|
||||
//
|
||||
private boolean loaded;
|
||||
|
||||
public NativeCode(String name, Supplier<? extends T> javaImpl, Supplier<? extends T> nativeImpl)
|
||||
{
|
||||
this( name, javaImpl, nativeImpl, false );
|
||||
}
|
||||
|
||||
public NativeCode(String name, Supplier<? extends T> javaImpl, Supplier<? extends T> nativeImpl, boolean extendedSupportCheck)
|
||||
{
|
||||
this.name = name;
|
||||
this.javaImpl = javaImpl;
|
||||
this.nativeImpl = nativeImpl;
|
||||
this.enableNativeFlag = Boolean.parseBoolean( System.getProperty( "net.md_5.bungee.jni." + name + ".enable", "true" ) );
|
||||
this.extendedSupportCheck = extendedSupportCheck;
|
||||
}
|
||||
|
||||
public T newInstance()
|
||||
@@ -32,8 +43,9 @@ public final class NativeCode<T>
|
||||
|
||||
public boolean load()
|
||||
{
|
||||
if ( !loaded && isSupported() )
|
||||
if ( enableNativeFlag && !loaded && isSupported() )
|
||||
{
|
||||
String name = this.name + ( isAarch64() ? "-arm" : "" );
|
||||
String fullName = "bungeecord-" + name;
|
||||
|
||||
try
|
||||
@@ -59,6 +71,13 @@ public final class NativeCode<T>
|
||||
}
|
||||
|
||||
System.load( temp.getPath() );
|
||||
|
||||
if ( extendedSupportCheck )
|
||||
{
|
||||
// Should throw NativeCodeException if incompatible
|
||||
nativeImpl.get();
|
||||
}
|
||||
|
||||
loaded = true;
|
||||
} catch ( IOException ex )
|
||||
{
|
||||
@@ -66,6 +85,9 @@ public final class NativeCode<T>
|
||||
} catch ( UnsatisfiedLinkError ex )
|
||||
{
|
||||
System.out.println( "Could not load native library: " + ex.getMessage() );
|
||||
} catch ( NativeCodeException ex )
|
||||
{
|
||||
System.out.println( "Native library " + name + " is incompatible: " + ex.getMessage() );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -73,8 +95,42 @@ public final class NativeCode<T>
|
||||
return loaded;
|
||||
}
|
||||
|
||||
public static boolean hasDirectBuffers()
|
||||
{
|
||||
ByteBuf directBuffer = null;
|
||||
boolean hasMemoryAddress = false;
|
||||
try
|
||||
{
|
||||
directBuffer = Unpooled.directBuffer();
|
||||
hasMemoryAddress = directBuffer.hasMemoryAddress();
|
||||
} finally
|
||||
{
|
||||
if ( directBuffer != null )
|
||||
{
|
||||
directBuffer.release();
|
||||
}
|
||||
}
|
||||
|
||||
return hasMemoryAddress;
|
||||
}
|
||||
|
||||
public static boolean isSupported()
|
||||
{
|
||||
return "Linux".equals( System.getProperty( "os.name" ) ) && "amd64".equals( System.getProperty( "os.arch" ) );
|
||||
return isSupportedPlatformAndArch() && hasDirectBuffers();
|
||||
}
|
||||
|
||||
private static boolean isSupportedPlatformAndArch()
|
||||
{
|
||||
return "Linux".equals( System.getProperty( "os.name" ) ) && ( isAmd64() || isAarch64() );
|
||||
}
|
||||
|
||||
private static boolean isAmd64()
|
||||
{
|
||||
return "amd64".equals( System.getProperty( "os.arch" ) );
|
||||
}
|
||||
|
||||
private static boolean isAarch64()
|
||||
{
|
||||
return "aarch64".equals( System.getProperty( "os.arch" ) );
|
||||
}
|
||||
}
|
||||
|
@@ -7,4 +7,10 @@ public class NativeCodeException extends RuntimeException
|
||||
{
|
||||
super( message + " : " + reason );
|
||||
}
|
||||
|
||||
public NativeCodeException(String message)
|
||||
{
|
||||
super( message );
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -18,4 +18,10 @@ public interface BungeeCipher
|
||||
void cipher(ByteBuf in, ByteBuf out) throws GeneralSecurityException;
|
||||
|
||||
ByteBuf cipher(ChannelHandlerContext ctx, ByteBuf in) throws GeneralSecurityException;
|
||||
|
||||
/*
|
||||
* This indicates whether the input ByteBuf is allowed to be a CompositeByteBuf.
|
||||
* If you need access to a memory address, you should not allow composite buffers.
|
||||
*/
|
||||
boolean allowComposite();
|
||||
}
|
||||
|
@@ -2,6 +2,7 @@ package net.md_5.bungee.jni.cipher;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.util.concurrent.FastThreadLocal;
|
||||
import java.security.GeneralSecurityException;
|
||||
import javax.crypto.Cipher;
|
||||
import javax.crypto.SecretKey;
|
||||
@@ -12,10 +13,10 @@ public class JavaCipher implements BungeeCipher
|
||||
{
|
||||
|
||||
private final Cipher cipher;
|
||||
private static final ThreadLocal<byte[]> heapInLocal = new EmptyByteThreadLocal();
|
||||
private static final ThreadLocal<byte[]> heapOutLocal = new EmptyByteThreadLocal();
|
||||
private static final FastThreadLocal<byte[]> heapInLocal = new EmptyByteThreadLocal();
|
||||
private static final FastThreadLocal<byte[]> heapOutLocal = new EmptyByteThreadLocal();
|
||||
|
||||
private static class EmptyByteThreadLocal extends ThreadLocal<byte[]>
|
||||
private static class EmptyByteThreadLocal extends FastThreadLocal<byte[]>
|
||||
{
|
||||
|
||||
@Override
|
||||
@@ -88,4 +89,10 @@ public class JavaCipher implements BungeeCipher
|
||||
in.readBytes( heapIn, 0, readableBytes );
|
||||
return heapIn;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean allowComposite()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@@ -37,9 +37,6 @@ public class NativeCipher implements BungeeCipher
|
||||
@Override
|
||||
public void cipher(ByteBuf in, ByteBuf out) throws GeneralSecurityException
|
||||
{
|
||||
// Smoke tests
|
||||
in.memoryAddress();
|
||||
out.memoryAddress();
|
||||
Preconditions.checkState( ctx != 0, "Invalid pointer to AES key!" );
|
||||
|
||||
// Store how many bytes we can cipher
|
||||
@@ -71,4 +68,10 @@ public class NativeCipher implements BungeeCipher
|
||||
|
||||
return heapOut;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean allowComposite()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@@ -6,9 +6,17 @@ import java.util.zip.DataFormatException;
|
||||
public interface BungeeZlib
|
||||
{
|
||||
|
||||
public static final int OUTPUT_BUFFER_SIZE = 8192;
|
||||
|
||||
void init(boolean compress, int level);
|
||||
|
||||
void free();
|
||||
|
||||
void process(ByteBuf in, ByteBuf out) throws DataFormatException;
|
||||
|
||||
/*
|
||||
* This indicates whether the input ByteBuf is allowed to be a CompositeByteBuf.
|
||||
* If you need access to a memory address, you should not allow composite buffers.
|
||||
*/
|
||||
boolean allowComposite();
|
||||
}
|
||||
|
@@ -73,4 +73,10 @@ public class JavaZlib implements BungeeZlib
|
||||
inflater.reset();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean allowComposite()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@@ -15,6 +15,8 @@ public class NativeCompressImpl
|
||||
|
||||
static native void initFields();
|
||||
|
||||
native boolean checkSupported();
|
||||
|
||||
native void end(long ctx, boolean compress);
|
||||
|
||||
native void reset(long ctx, boolean compress);
|
||||
|
@@ -15,6 +15,14 @@ public class NativeZlib implements BungeeZlib
|
||||
private boolean compress;
|
||||
private long ctx;
|
||||
|
||||
public NativeZlib()
|
||||
{
|
||||
if ( !nativeCompress.checkSupported() )
|
||||
{
|
||||
throw new NativeCodeException( "This CPU does not support the required SSE 4.2 and/or PCLMUL extensions!" );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(boolean compress, int level)
|
||||
{
|
||||
@@ -40,14 +48,17 @@ public class NativeZlib implements BungeeZlib
|
||||
@Override
|
||||
public void process(ByteBuf in, ByteBuf out) throws DataFormatException
|
||||
{
|
||||
// Smoke tests
|
||||
in.memoryAddress();
|
||||
out.memoryAddress();
|
||||
Preconditions.checkState( ctx != 0, "Invalid pointer to compress!" );
|
||||
|
||||
while ( !nativeCompress.finished && ( compress || in.isReadable() ) )
|
||||
{
|
||||
out.ensureWritable( 8192 );
|
||||
if ( compress )
|
||||
{
|
||||
out.ensureWritable( OUTPUT_BUFFER_SIZE );
|
||||
} else
|
||||
{
|
||||
Preconditions.checkArgument( out.isWritable(), "Output buffer overrun" );
|
||||
}
|
||||
|
||||
int processed;
|
||||
try
|
||||
@@ -66,4 +77,10 @@ public class NativeZlib implements BungeeZlib
|
||||
nativeCompress.consumed = 0;
|
||||
nativeCompress.finished = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean allowComposite()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
BIN
native/src/main/resources/native-cipher-arm.so
Executable file
BIN
native/src/main/resources/native-cipher-arm.so
Executable file
Binary file not shown.
Binary file not shown.
BIN
native/src/main/resources/native-compress-arm.so
Executable file
BIN
native/src/main/resources/native-compress-arm.so
Executable file
Binary file not shown.
Binary file not shown.
@@ -15,7 +15,7 @@ import org.junit.jupiter.api.Test;
|
||||
public class NativeZlibTest
|
||||
{
|
||||
|
||||
private final NativeCode<BungeeZlib> factory = new NativeCode<>( "native-compress", JavaZlib::new, NativeZlib::new );
|
||||
private final NativeCode<BungeeZlib> factory = new NativeCode<>( "native-compress", JavaZlib::new, NativeZlib::new, true );
|
||||
|
||||
@Test
|
||||
public void doTest() throws DataFormatException
|
||||
@@ -64,7 +64,7 @@ public class NativeZlibTest
|
||||
|
||||
zlib.process( originalBuf, compressed );
|
||||
|
||||
ByteBuf uncompressed = Unpooled.directBuffer();
|
||||
ByteBuf uncompressed = Unpooled.directBuffer( dataBuf.length, dataBuf.length );
|
||||
|
||||
zlib.init( false, 0 );
|
||||
zlib.process( compressed, uncompressed );
|
||||
|
28
nbt/LICENSE
Normal file
28
nbt/LICENSE
Normal file
@@ -0,0 +1,28 @@
|
||||
BSD 3-Clause License
|
||||
|
||||
Copyright (c) 2025, SpigotMC Pty. Ltd.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
3. Neither the name of the copyright holder nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
4
nbt/README.md
Normal file
4
nbt/README.md
Normal file
@@ -0,0 +1,4 @@
|
||||
BungeeCord-NBT
|
||||
=================
|
||||
|
||||
Minimal implementation of NBT for use in BungeeCord
|
31
nbt/nb-configuration.xml
Normal file
31
nbt/nb-configuration.xml
Normal file
@@ -0,0 +1,31 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project-shared-configuration>
|
||||
<!--
|
||||
This file contains additional configuration written by modules in the NetBeans IDE.
|
||||
The configuration is intended to be shared among all the users of project and
|
||||
therefore it is assumed to be part of version control checkout.
|
||||
Without this configuration present, some functionality in the IDE may be limited or fail altogether.
|
||||
-->
|
||||
<properties xmlns="http://www.netbeans.org/ns/maven-properties-data/1">
|
||||
<!--
|
||||
Properties that influence various parts of the IDE, especially code formatting and the like.
|
||||
You can copy and paste the single properties, into the pom.xml file and the IDE will pick them up.
|
||||
That way multiple projects can share the same settings (useful for formatting rules for example).
|
||||
Any value defined here will override the pom.xml file value but is only applicable to the current project.
|
||||
-->
|
||||
<org-netbeans-modules-editor-indent.CodeStyle.usedProfile>project</org-netbeans-modules-editor-indent.CodeStyle.usedProfile>
|
||||
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.classDeclBracePlacement>NEW_LINE</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.classDeclBracePlacement>
|
||||
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.otherBracePlacement>NEW_LINE</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.otherBracePlacement>
|
||||
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.methodDeclBracePlacement>NEW_LINE</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.methodDeclBracePlacement>
|
||||
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinMethodCallParens>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinMethodCallParens>
|
||||
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinSwitchParens>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinSwitchParens>
|
||||
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinCatchParens>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinCatchParens>
|
||||
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinTryParens>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinTryParens>
|
||||
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinSynchronizedParens>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinSynchronizedParens>
|
||||
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinArrayInitBrackets>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinArrayInitBrackets>
|
||||
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinParens>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinParens>
|
||||
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinWhileParens>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinWhileParens>
|
||||
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinIfParens>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinIfParens>
|
||||
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinForParens>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinForParens>
|
||||
</properties>
|
||||
</project-shared-configuration>
|
27
nbt/pom.xml
Normal file
27
nbt/pom.xml
Normal file
@@ -0,0 +1,27 @@
|
||||
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>net.md-5</groupId>
|
||||
<artifactId>bungeecord-parent</artifactId>
|
||||
<version>1.21-R0.4-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
<groupId>net.md-5</groupId>
|
||||
<artifactId>bungeecord-nbt</artifactId>
|
||||
<version>1.21-R0.4-SNAPSHOT</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>BungeeCord-NBT</name>
|
||||
<description>Minimal implementation of NBT for use in BungeeCord</description>
|
||||
<licenses>
|
||||
<license>
|
||||
<name>BSD-3-Clause</name>
|
||||
<url>https://github.com/SpigotMC/BungeeCord/blob/master/nbt/LICENSE</url>
|
||||
<distribution>repo</distribution>
|
||||
</license>
|
||||
</licenses>
|
||||
</project>
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user