Compare commits

...

50 Commits

Author SHA1 Message Date
2a19bfb6d3 new event TabCompleteRequestEvent and deprecate TabCompleteEvent 2025-04-04 22:58:24 +02:00
451068694a Add CommandsDeclareEvent to declare commands with brigadier API 2025-04-04 22:58:24 +02:00
e7e77e2731 Server branding now includes the backend server name 2025-04-04 22:58:23 +02:00
d6bf6a13f4 Multi-session with same Minecraft account with specific permission
Players with permission bungeecord.multiple_connect can have multiple connections with the same Minecraft account.
The UUID and player name is altered to avoid collision with other player:
UUID : xxxxxxxx-xxxx-VIxx-xxxx-xxxxxxxxxxxx
- The UUID version (V above) is now the provided version + 8 (for online player, it is 4, so it becomes C).
- The I digit will follow the index of the duplicated player : first duplicated player is 1, second one is 2.
- The name of the player will be the real player name, followed by the character "." (dot) followed by the duplication index.

Bedrock accounts connected using the Floodgate plugin will not be able to connect multiple times due to the risk of xUID collision.
2025-04-04 22:58:13 +02:00
8dd77af343 Change projet configuration and POM for Pandacube 2025-04-04 22:52:02 +02:00
fb9e1b0499 Remove modules and startup delay
We don’t need them for Pandacube
2025-04-04 22:51:57 +02:00
md_5
f6151dce56
Bump version to 1.21-R0.3-SNAPSHOT 2025-03-28 19:52:19 +11:00
md_5
9667743735
Release 1.21-R0.2 2025-03-28 19:49:50 +11:00
md_5
7587f03306
SPIGOT-8024, #3811, #3812: Add versioned chat serialization (beta) 2025-03-28 07:01:06 +11:00
md_5
77b81f2612
Bump Netty to 4.1.119.Final 2025-03-27 06:58:23 +11:00
md_5
fa6d47732d
Bump version to 1.21-R0.2-SNAPSHOT 2025-03-26 19:38:24 +11:00
md_5
252e7b0027
Release 1.21-R0.1 2025-03-26 19:33:00 +11:00
md_5
c820b3a062
Minecraft 1.21.5 support 2025-03-26 03:05:00 +11:00
Outfluencer
687c302610
Add WriteTimeoutHandler 2025-03-23 08:19:07 +11:00
Outfluencer
4b0262312e
#3805: Remove redundant PluginMessage handler in InitialHandler
There is no state in wich we could decode a PluginMessage in InitialHandler, so we should remove it here.
2025-03-22 17:33:46 +11:00
md_5
6f13c2d6b6
Minecraft 1.21.5-rc1 protocol support 2025-03-21 07:34:08 +11:00
md_5
cd186999e5
Minecraft 1.21.5-pre3 protocol support 2025-03-19 06:20:53 +11:00
Outfluencer
e3c7fd8cc5
#3801: Add support for 1.21.5-pre2 2025-03-13 09:19:09 +11:00
Outfluencer
9476ffccdb
#3797: Expose sendPacketQueued to unsafe interface 2025-03-08 16:56:20 +11:00
md_5
47f8c29a7c
Minecraft 25w10a protocol support 2025-03-08 09:22:17 +11:00
Outfluencer
458246505f
Don't attempt to read packets after channel is closed/closing 2025-03-03 19:58:46 +11:00
Julian Vennen
362bd0f4c4
#3794: Ensure listType is set to compound tag when wrapping tags 2025-03-01 14:48:59 +11:00
md_5
598d73e6f0
Minecraft 25w09b protocol support 2025-03-01 14:21:52 +11:00
md_5
f797bd488f
Minecraft 25w08a protocol support 2025-02-21 19:18:25 +11:00
md_5
0d153feee7
#3791: Handle third party tools rewriting integers badly 2025-02-18 19:17:31 +11:00
Outfluencer
2a78233cc2
#3790: Implement toString for ProxyReloadEvent
Its the only event that doesn't have it
2025-02-18 19:09:54 +11:00
Valentine
591e18753d
#3786: Fix missing compressor release when removing LengthPrependerAndCompressor handler 2025-02-16 07:44:55 +11:00
Outfluencer
556a15a6f8
#3733: Add incoming packet configuration options 2025-02-15 17:26:15 +11:00
Janmm14
774a6fd68c
#3766: Combine packet length prepending and compressor 2025-02-15 15:20:00 +11:00
Outfluencer
0070421549
#3776: Expose ChannelInitializerHolder in protocol module 2025-02-15 14:52:29 +11:00
md_5
4dad940a2f
Extract bungee plugin message channel to constant 2025-02-12 07:09:05 +11:00
Valentine
ed4a80eb0b
#3781: Fix eventLoopCallback 2025-02-09 08:17:16 +11:00
md_5
dd2033bf1a
Update maven-checkstyle-plugin 2025-02-08 17:59:56 +11:00
md_5
cceebdad2a
#3780: Allow empty scoreboard nametag visibility 2025-02-08 17:55:18 +11:00
md_5
05bdf5d3c1
Set compiler proc=full on Java 21+ 2025-02-08 16:00:53 +11:00
Outfluencer
c3e4a6ef5b
#3779: Improve eventloop consistency and isClosing code 2025-02-08 15:50:43 +11:00
md_5
2337acfcc1
Minecraft 25w06a protocol support 2025-02-08 08:30:30 +11:00
Outfluencer
69861e5334
#3778: Null check team collision rule because it does not exist in 1.8 2025-02-07 07:06:45 +11:00
md_5
22aa6f5faf
Use lombok for Team packet 2025-02-02 08:35:39 +11:00
md_5
508c2f7ac3
Minecraft 25w05a protocol support 2025-02-01 12:50:31 +11:00
md_5
9dd5fb626d
Fix duplicate 25w04a packet mapping 2025-01-30 07:43:05 +11:00
BoomEaro
b5ae0196fc
#3771: Make PluginManager methods thread safe 2025-01-29 20:38:20 +11:00
Outfluencer
80bb237289
#3774: Minecraft 25w04a chat component changes 2025-01-29 20:35:46 +11:00
Outfluencer
4fded9828f
#3775: Allow decompressed packets to grow to max capacity
Do not use size as max capacity, as its possible that the entity
rewriter increases the size afterwards. This would result in a kick (it
happens rarely as the entity ids size must differ).
2025-01-29 07:52:09 +11:00
md_5
6b22690971
Minecraft 25w04a protocol support 2025-01-27 20:09:01 +11:00
md_5
60a3bf082f
Preallocate compression output buffer to remove unnecessary resizing 2025-01-27 20:08:58 +11:00
Outfluencer
0aa2871b26
#3761: Whitelist LoginPayloadResponse in UpstreamBridge#shouldHandle
Required for #3758 to function correctly.
2025-01-23 07:11:11 +11:00
Outfluencer
1265a9927b
#3769: Fix possible NoSuchElementException changing compression 2025-01-07 20:46:20 +11:00
md_5
d99570214a
Update date in README.md 2025-01-01 10:19:38 +11:00
Janmm14
15bd33b25b
#3767: Remove explicit direct buffer check 2024-12-14 09:46:35 +11:00
117 changed files with 2751 additions and 2218 deletions

View File

@ -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.

View File

@ -4,15 +4,14 @@
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>net.md-5</groupId>
<groupId>fr.pandacube.bungeecord</groupId>
<artifactId>bungeecord-parent</artifactId>
<version>1.21-R0.1-SNAPSHOT</version>
<version>1.21-R0.3-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.3-SNAPSHOT</version>
<packaging>jar</packaging>
<name>BungeeCord-API</name>
@ -20,25 +19,25 @@
<dependencies>
<dependency>
<groupId>net.md-5</groupId>
<groupId>fr.pandacube.bungeecord</groupId>
<artifactId>bungeecord-chat</artifactId>
<version>${project.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>net.md-5</groupId>
<groupId>fr.pandacube.bungeecord</groupId>
<artifactId>bungeecord-config</artifactId>
<version>${project.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>net.md-5</groupId>
<groupId>fr.pandacube.bungeecord</groupId>
<artifactId>bungeecord-event</artifactId>
<version>${project.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>net.md-5</groupId>
<groupId>fr.pandacube.bungeecord</groupId>
<artifactId>bungeecord-protocol</artifactId>
<version>${project.version}</version>
<scope>compile</scope>

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -0,0 +1,157 @@
package net.md_5.bungee.api.event;
import com.mojang.brigadier.arguments.ArgumentType;
import com.mojang.brigadier.arguments.IntegerArgumentType;
import com.mojang.brigadier.arguments.StringArgumentType;
import com.mojang.brigadier.builder.ArgumentBuilder;
import com.mojang.brigadier.builder.RequiredArgumentBuilder;
import com.mojang.brigadier.suggestion.SuggestionProvider;
import com.mojang.brigadier.tree.CommandNode;
import com.mojang.brigadier.tree.RootCommandNode;
import lombok.AccessLevel;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.Setter;
import lombok.ToString;
import net.md_5.bungee.api.CommandSender;
import net.md_5.bungee.api.connection.Connection;
import net.md_5.bungee.api.plugin.Command;
import net.md_5.bungee.api.plugin.Plugin;
import net.md_5.bungee.api.plugin.PluginManager;
import net.md_5.bungee.api.plugin.TabExecutor;
/**
* Event called when a downstream server (on 1.13+) sends the command structure
* to a player, but before BungeeCord adds the dummy command nodes of
* registered commands.
* <p>
* BungeeCord will not overwrite the modifications made by the listeners.
*
* <h2>Usage example</h2>
* Here is a usage example of this event, to declare a command structure.
* This illustrates the commands /server and /send of Bungee.
* <pre>
* event.getRoot().addChild( LiteralArgumentBuilder.&lt;CommandSender&gt;literal( "server" )
* .requires( sender -&gt; sender.hasPermission( "bungeecord.command.server" ) )
* .executes( a -&gt; 0 )
* .then( RequiredArgumentBuilder.argument( "serverName", StringArgumentType.greedyString() )
* .suggests( SuggestionRegistry.ASK_SERVER )
* )
* .build()
* );
* event.getRoot().addChild( LiteralArgumentBuilder.&lt;CommandSender&gt;literal( "send" )
* .requires( sender -&gt; sender.hasPermission( "bungeecord.command.send" ) )
* .then( RequiredArgumentBuilder.argument( "playerName", StringArgumentType.word() )
* .suggests( SuggestionRegistry.ASK_SERVER )
* .then( RequiredArgumentBuilder.argument( "serverName", StringArgumentType.greedyString() )
* .suggests( SuggestionRegistry.ASK_SERVER )
* )
* )
* .build()
* );
* </pre>
*
* <h2>Flag a {@link CommandNode} as executable or not</h2>
* The implementation of a {@link com.mojang.brigadier.Command Command} used in
* {@link ArgumentBuilder#executes(com.mojang.brigadier.Command)} will never be
* executed. This will only tell to the client if the current node is
* executable or not.
* <ul>
* <li>
* {@code builder.executes(null)} (default) to mark the node as not
* executable.
* </li>
* <li>
* {@code builder.executes(a -> 0)}, or any non null argument, to mark
* the node as executable (the child arguments are displayed as
* optional).
* </li>
* </ul>
*
* <h2>{@link CommandNode}s suggestions management</h2>
* The implementation of a SuggestionProvider used in
* {@link RequiredArgumentBuilder#suggests(SuggestionProvider)} will never be
* executed. This will only tell to the client how to deal with the
* auto-completion of the argument.
* <ul>
* <li>
* {@code builder.suggests(null)} (default) to disable auto-completion
* for this argument.
* </li>
* <li>
* {@code builder.suggests(SuggestionRegistry.ALL_RECIPES)} to suggest
* Minecrafts recipes.
* </li>
* <li>
* {@code builder.suggests(SuggestionRegistry.AVAILABLE_SOUNDS)} to
* suggest Minecrafts default sound identifiers.
* </li>
* <li>
* {@code builder.suggests(SuggestionRegistry.SUMMONABLE_ENTITIES)} to
* suggest Minecrafts default summonable entities identifiers.
* </li>
* <li>
* {@code builder.suggests(SuggestionRegistry.ASK_SERVER)}, or any
* other non null argument, to make the Minecraft client ask
* auto-completion to the server. Any specified implementation of
* {@link SuggestionProvider} will never be executed.
* </li>
* </ul>
*
* <h2>Argument types</h2>
* When building a new argument command node using
* {@link RequiredArgumentBuilder#argument(String, ArgumentType)}, you have to
* specify an {@link ArgumentType}. You can use all subclasses of
* {@link ArgumentType} provided with brigadier (for instance,
* {@link StringArgumentType} or {@link IntegerArgumentType}), or call any
* {@code ArgumentRegistry.minecraft*()} methods to use a {@code minecraft:*}
* argument type.
*
* <h2>Limitations with brigadier API</h2>
* This event is only used for the client to show command syntax, suggest
* sub-commands and color the arguments in the chat box. The command execution
* needs to be implemented using {@link PluginManager#registerCommand(Plugin,
* Command)} and the server-side tab-completion using {@link TabCompleteEvent}
* or {@link TabExecutor}.
*/
@Data
@ToString(callSuper = true)
@EqualsAndHashCode(callSuper = true)
public class CommandsDeclareEvent extends TargetedEvent
{
/**
* Wether or not the command tree is modified by this event.
*
* If this value is set to true, BungeeCord will ensure that the
* modifications made in the command tree, will be sent to the player.
* If this is false, the modifications may not be taken into account.
*
* When calling {@link #getRoot()}, this value is automatically set
* to true.
*/
@Setter(value = AccessLevel.NONE)
private boolean modified = false;
/**
* The root command node of the command structure that will be send to the
* player.
*/
private final RootCommandNode<CommandSender> root;
public CommandsDeclareEvent(Connection sender, Connection receiver, RootCommandNode<CommandSender> root)
{
super( sender, receiver );
this.root = root;
}
/**
* The root command node of the command structure that will be send to the
* player.
* @return The root command node
*/
public RootCommandNode<CommandSender> getRoot()
{
modified = true;
return root;
}
}

View File

@ -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

View File

@ -9,7 +9,9 @@ import net.md_5.bungee.api.plugin.Cancellable;
/**
* Event called when a player uses tab completion.
* @deprecated please use {@link TabCompleteRequestEvent} to support 1.13+ suggestions.
*/
@Deprecated
@Data
@ToString(callSuper = true)
@EqualsAndHashCode(callSuper = true)

View File

@ -0,0 +1,85 @@
package net.md_5.bungee.api.event;
import com.google.common.base.Preconditions;
import com.mojang.brigadier.context.StringRange;
import com.mojang.brigadier.suggestion.Suggestions;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import net.md_5.bungee.api.connection.Connection;
import net.md_5.bungee.api.connection.ProxiedPlayer;
import net.md_5.bungee.api.plugin.Cancellable;
import net.md_5.bungee.protocol.ProtocolConstants;
/**
* Event called when a player uses tab completion.
*/
@Data
@ToString(callSuper = true)
@EqualsAndHashCode(callSuper = true)
public class TabCompleteRequestEvent extends TargetedEvent implements Cancellable
{
/**
* Cancelled state.
*/
private boolean cancelled;
/**
* The message the player has already entered.
*/
private final String cursor;
/**
* Range corresponding to the last word of {@link #getCursor()}.
* If you want your suggestions to be compatible with 1.12 and older
* clients, you need to {@link #setSuggestions(Suggestions)} with
* a range equals to this one.
* For 1.13 and newer clients, any other range that cover any part of
* {@link #getCursor()} is fine.<br>
* To check if the client supports custom ranges, use
* {@link #supportsCustomRange()}.
*/
private final StringRange legacyCompatibleRange;
/**
* The suggestions that will be sent to the client. If this list is empty,
* the request will be forwarded to the server.
*/
private Suggestions suggestions;
public TabCompleteRequestEvent(Connection sender, Connection receiver, String cursor, StringRange legacyCompatibleRange, Suggestions suggestions)
{
super( sender, receiver );
this.cursor = cursor;
this.legacyCompatibleRange = legacyCompatibleRange;
this.suggestions = suggestions;
}
/**
* Sets the suggestions that will be sent to the client.
* If this list is empty, the request will be forwarded to the server.
* @param suggestions the new Suggestions. Cannot be null.
* @throws IllegalArgumentException if the client is on 1.12 or lower and
* {@code suggestions.getRange()} is not equals to {@link #legacyCompatibleRange}.
*/
public void setSuggestions(Suggestions suggestions)
{
Preconditions.checkNotNull( suggestions );
Preconditions.checkArgument( supportsCustomRange() || legacyCompatibleRange.equals( suggestions.getRange() ),
"Clients on 1.12 or lower versions don't support the provided range for tab-completion: " + suggestions.getRange()
+ ". Please use TabCompleteRequestEvent.getLegacyCompatibleRange() for legacy clients." );
this.suggestions = suggestions;
}
/**
* Convenient method to tell if the client supports custom range for
* suggestions.
* If the client is on 1.13 or above, this methods returns true, and any
* range can be used for {@link #setSuggestions(Suggestions)}. Otherwise,
* it returns false and the defined range must be equals to
* {@link #legacyCompatibleRange}.
* @return true if the client is on 1.13 or newer version, false otherwise.
*/
public boolean supportsCustomRange()
{
return ( (ProxiedPlayer) getSender() ).getPendingConnection().getVersion() >= ProtocolConstants.MINECRAFT_1_13;
}
}

View File

@ -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)

View File

@ -4,15 +4,14 @@
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>net.md-5</groupId>
<groupId>fr.pandacube.bungeecord</groupId>
<artifactId>bungeecord-parent</artifactId>
<version>1.21-R0.1-SNAPSHOT</version>
<version>1.21-R0.3-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.3-SNAPSHOT</version>
<packaging>jar</packaging>
<name>BungeeCord-Bootstrap</name>
@ -21,14 +20,12 @@
<properties>
<maven.deploy.skip>true</maven.deploy.skip>
<maven.javadoc.skip>true</maven.javadoc.skip>
<maven.compiler.source>1.6</maven.compiler.source>
<maven.compiler.target>1.6</maven.compiler.target>
<maven.build.timestamp.format>yyyyMMdd</maven.build.timestamp.format>
</properties>
<dependencies>
<dependency>
<groupId>net.md-5</groupId>
<groupId>fr.pandacube.bungeecord</groupId>
<artifactId>bungeecord-proxy</artifactId>
<version>${project.version}</version>
<scope>compile</scope>
@ -36,7 +33,7 @@
</dependencies>
<build>
<finalName>BungeeCord</finalName>
<finalName>BungeeCord-${project.version}-${build.number}</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>

View File

@ -4,15 +4,14 @@
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>net.md-5</groupId>
<groupId>fr.pandacube.bungeecord</groupId>
<artifactId>bungeecord-parent</artifactId>
<version>1.21-R0.1-SNAPSHOT</version>
<version>1.21-R0.3-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.3-SNAPSHOT</version>
<packaging>jar</packaging>
<name>BungeeCord-Chat</name>

View File

@ -13,7 +13,8 @@ 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 net.md_5.bungee.chat.VersionedComponentSerializer;
import org.jetbrains.annotations.ApiStatus;
@Getter
@ToString
@ -34,6 +35,7 @@ public final class HoverEvent
* Returns whether this hover event is prior to 1.16
*/
@Setter
@ApiStatus.Internal
private boolean legacy = false;
/**
@ -80,7 +82,7 @@ public final class HoverEvent
return (BaseComponent[]) ( (Text) content ).getValue();
}
TextComponent component = new TextComponent( ComponentSerializer.toString( content ) );
TextComponent component = new TextComponent( VersionedComponentSerializer.getDefault().toString( content ) );
return new BaseComponent[]
{
component

View File

@ -10,27 +10,36 @@ import com.google.gson.JsonSerializer;
import java.lang.reflect.Type;
import java.util.UUID;
import net.md_5.bungee.api.chat.BaseComponent;
import net.md_5.bungee.chat.BaseComponentSerializer;
import net.md_5.bungee.chat.VersionedComponentSerializer;
public class EntitySerializer implements JsonSerializer<Entity>, JsonDeserializer<Entity>
public class EntitySerializer extends BaseComponentSerializer implements JsonSerializer<Entity>, JsonDeserializer<Entity>
{
public EntitySerializer(VersionedComponentSerializer serializer)
{
super( serializer );
}
@Override
public Entity deserialize(JsonElement element, Type type, JsonDeserializationContext context) throws JsonParseException
{
JsonObject value = element.getAsJsonObject();
boolean newEntity = value.has( "uuid" );
String idString;
JsonElement id = value.get( "id" );
if ( id.isJsonArray() )
JsonElement uuid = value.get( newEntity ? "uuid" : "id" );
if ( uuid.isJsonArray() )
{
idString = parseUUID( context.deserialize( id, int[].class ) ).toString();
idString = parseUUID( context.deserialize( uuid, int[].class ) ).toString();
} else
{
idString = id.getAsString();
idString = uuid.getAsString();
}
return new Entity(
( value.has( "type" ) ) ? value.get( "type" ).getAsString() : null,
( value.has( newEntity ? "id" : "type" ) ) ? value.get( newEntity ? "id" : "type" ).getAsString() : null,
idString,
( value.has( "name" ) ) ? context.deserialize( value.get( "name" ), BaseComponent.class ) : null
);
@ -40,8 +49,21 @@ public class EntitySerializer implements JsonSerializer<Entity>, JsonDeserialize
public JsonElement serialize(Entity content, Type type, JsonSerializationContext context)
{
JsonObject object = new JsonObject();
switch ( serializer.getVersion() )
{
case V1_21_5:
object.addProperty( "id", ( content.getType() != null ) ? content.getType() : "minecraft:pig" );
object.addProperty( "uuid", content.getId() );
break;
case V1_16:
object.addProperty( "type", ( content.getType() != null ) ? content.getType() : "minecraft:pig" );
object.addProperty( "id", content.getId() );
break;
default:
throw new IllegalArgumentException( "Unknown version " + serializer.getVersion() );
}
if ( content.getName() != null )
{
object.add( "name", context.serialize( content.getName() ) );

View File

@ -10,15 +10,19 @@ import java.util.Arrays;
import java.util.Collections;
import java.util.IdentityHashMap;
import java.util.Locale;
import lombok.RequiredArgsConstructor;
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;
@RequiredArgsConstructor
public class BaseComponentSerializer
{
protected final VersionedComponentSerializer serializer;
protected void deserialize(JsonObject object, BaseComponent component, JsonDeserializationContext context)
{
component.applyStyle( context.deserialize( object, ComponentStyle.class ) );
@ -30,23 +34,82 @@ public class BaseComponentSerializer
}
//Events
JsonObject clickEvent = object.getAsJsonObject( "clickEvent" );
JsonObject clickEvent;
boolean newClickEvent = ( clickEvent = object.getAsJsonObject( "click_event" ) ) != null;
if ( !newClickEvent )
{
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() : "" ) );
ClickEvent.Action action = ClickEvent.Action.valueOf( clickEvent.get( "action" ).getAsString().toUpperCase( Locale.ROOT ) );
if ( newClickEvent )
{
switch ( action )
{
case OPEN_URL:
component.setClickEvent( new ClickEvent( action, clickEvent.get( "url" ).getAsString() ) );
break;
case RUN_COMMAND:
case SUGGEST_COMMAND:
component.setClickEvent( new ClickEvent( action, clickEvent.get( "command" ).getAsString() ) );
break;
case CHANGE_PAGE:
int page = clickEvent.get( "page" ).getAsInt();
Preconditions.checkArgument( page >= 0, "Page number has to be positive" );
component.setClickEvent( new ClickEvent( action, Integer.toString( page ) ) );
break;
default:
component.setClickEvent( new ClickEvent( action, ( clickEvent.has( "value" ) ) ? clickEvent.get( "value" ).getAsString() : "" ) );
break;
}
JsonObject hoverEventJson = object.getAsJsonObject( "hoverEvent" );
} else
{
component.setClickEvent( new ClickEvent( action, ( clickEvent.has( "value" ) ) ? clickEvent.get( "value" ).getAsString() : "" ) );
}
}
JsonObject hoverEventJson;
boolean newHoverEvent = ( hoverEventJson = object.getAsJsonObject( "hover_event" ) ) != null;
if ( !newHoverEvent )
{
hoverEventJson = object.getAsJsonObject( "hoverEvent" );
}
if ( hoverEventJson != null )
{
HoverEvent hoverEvent = null;
HoverEvent.Action action = HoverEvent.Action.valueOf( hoverEventJson.get( "action" ).getAsString().toUpperCase( Locale.ROOT ) );
if ( newHoverEvent || hoverEventJson.has( "contents" ) )
{
// value is only used for text in >= 1.21.5 (its inlined now)
JsonElement contents = hoverEventJson.get( newHoverEvent ? "value" : "contents" );
if ( contents != null || ( newHoverEvent && ( action == HoverEvent.Action.SHOW_ITEM || action == HoverEvent.Action.SHOW_ENTITY ) ) )
{
if ( contents == null )
{
// this is the new inline for SHOW_ITEM and SHOW_ENTITY
contents = hoverEventJson;
}
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 ) ) );
}
} else
{
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;
@ -61,23 +124,6 @@ public class BaseComponentSerializer
};
}
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 ) ) );
}
}
@ -97,15 +143,15 @@ public class BaseComponentSerializer
protected void serialize(JsonObject object, BaseComponent component, JsonSerializationContext context)
{
boolean first = false;
if ( ComponentSerializer.serializedComponents.get() == null )
if ( VersionedComponentSerializer.serializedComponents.get() == null )
{
first = true;
ComponentSerializer.serializedComponents.set( Collections.newSetFromMap( new IdentityHashMap<BaseComponent, Boolean>() ) );
VersionedComponentSerializer.serializedComponents.set( Collections.newSetFromMap( new IdentityHashMap<BaseComponent, Boolean>() ) );
}
try
{
Preconditions.checkArgument( !ComponentSerializer.serializedComponents.get().contains( component ), "Component loop" );
ComponentSerializer.serializedComponents.get().add( component );
Preconditions.checkArgument( !VersionedComponentSerializer.serializedComponents.get().contains( component ), "Component loop" );
VersionedComponentSerializer.serializedComponents.get().add( component );
ComponentStyleSerializer.serializeTo( component.getStyle(), object );
@ -118,9 +164,38 @@ public class BaseComponentSerializer
if ( component.getClickEvent() != null )
{
JsonObject clickEvent = new JsonObject();
clickEvent.addProperty( "action", component.getClickEvent().getAction().toString().toLowerCase( Locale.ROOT ) );
String actionName = component.getClickEvent().getAction().toString().toLowerCase( Locale.ROOT );
clickEvent.addProperty( "action", actionName.toLowerCase( Locale.ROOT ) );
switch ( serializer.getVersion() )
{
case V1_21_5:
ClickEvent.Action action = ClickEvent.Action.valueOf( actionName.toUpperCase( Locale.ROOT ) );
switch ( action )
{
case OPEN_URL:
clickEvent.addProperty( "url", component.getClickEvent().getValue() );
break;
case RUN_COMMAND:
case SUGGEST_COMMAND:
clickEvent.addProperty( "command", component.getClickEvent().getValue() );
break;
case CHANGE_PAGE:
clickEvent.addProperty( "page", Integer.parseInt( component.getClickEvent().getValue() ) );
break;
default:
clickEvent.addProperty( "value", component.getClickEvent().getValue() );
break;
}
object.add( "click_event", clickEvent );
break;
case V1_16:
clickEvent.addProperty( "value", component.getClickEvent().getValue() );
object.add( "clickEvent", clickEvent );
break;
default:
throw new IllegalArgumentException( "Unknown version " + serializer.getVersion() );
}
}
if ( component.getHoverEvent() != null )
{
@ -131,10 +206,39 @@ public class BaseComponentSerializer
hoverEvent.add( "value", context.serialize( component.getHoverEvent().getContents().get( 0 ) ) );
} else
{
hoverEvent.add( "contents", context.serialize( ( component.getHoverEvent().getContents().size() == 1 )
switch ( serializer.getVersion() )
{
case V1_21_5:
if ( component.getHoverEvent().getAction() == HoverEvent.Action.SHOW_ITEM || component.getHoverEvent().getAction() == HoverEvent.Action.SHOW_ENTITY )
{
JsonObject inlined = context.serialize( ( component.getHoverEvent().getContents().size() == 1 )
? component.getHoverEvent().getContents().get( 0 ) : component.getHoverEvent().getContents() ).getAsJsonObject();
inlined.entrySet().forEach( entry -> hoverEvent.add( entry.getKey(), entry.getValue() ) );
} else
{
hoverEvent.add( "value", context.serialize( ( component.getHoverEvent().getContents().size() == 1 )
? component.getHoverEvent().getContents().get( 0 ) : component.getHoverEvent().getContents() ) );
}
break;
case V1_16:
hoverEvent.add( "contents", context.serialize( ( component.getHoverEvent().getContents().size() == 1 )
? component.getHoverEvent().getContents().get( 0 ) : component.getHoverEvent().getContents() ) );
break;
default:
throw new IllegalArgumentException( "Unknown version " + serializer.getVersion() );
}
}
switch ( serializer.getVersion() )
{
case V1_21_5:
object.add( "hover_event", hoverEvent );
break;
case V1_16:
object.add( "hoverEvent", hoverEvent );
break;
default:
throw new IllegalArgumentException( "Unknown version " + serializer.getVersion() );
}
}
if ( component.getExtra() != null )
@ -143,10 +247,10 @@ public class BaseComponentSerializer
}
} finally
{
ComponentSerializer.serializedComponents.get().remove( component );
VersionedComponentSerializer.serializedComponents.get().remove( component );
if ( first )
{
ComponentSerializer.serializedComponents.set( null );
VersionedComponentSerializer.serializedComponents.set( null );
}
}
}

View File

@ -0,0 +1,10 @@
package net.md_5.bungee.chat;
import org.jetbrains.annotations.ApiStatus;
@ApiStatus.Internal
public enum ChatVersion
{
V1_16,
V1_21_5;
}

View File

@ -1,52 +1,17 @@
package net.md_5.bungee.chat;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonArray;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;
import com.google.gson.JsonParser;
import com.google.gson.JsonPrimitive;
import java.lang.reflect.Type;
import java.util.Set;
import net.md_5.bungee.api.chat.BaseComponent;
import net.md_5.bungee.api.chat.ComponentStyle;
import net.md_5.bungee.api.chat.ItemTag;
import net.md_5.bungee.api.chat.KeybindComponent;
import net.md_5.bungee.api.chat.ScoreComponent;
import net.md_5.bungee.api.chat.SelectorComponent;
import net.md_5.bungee.api.chat.TextComponent;
import net.md_5.bungee.api.chat.TranslatableComponent;
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.EntitySerializer;
import net.md_5.bungee.api.chat.hover.content.Item;
import net.md_5.bungee.api.chat.hover.content.ItemSerializer;
import net.md_5.bungee.api.chat.hover.content.Text;
import net.md_5.bungee.api.chat.hover.content.TextSerializer;
public class ComponentSerializer implements JsonDeserializer<BaseComponent>
{
private static final Gson gson = new GsonBuilder().
registerTypeAdapter( BaseComponent.class, new ComponentSerializer() ).
registerTypeAdapter( TextComponent.class, new TextComponentSerializer() ).
registerTypeAdapter( TranslatableComponent.class, new TranslatableComponentSerializer() ).
registerTypeAdapter( KeybindComponent.class, new KeybindComponentSerializer() ).
registerTypeAdapter( ScoreComponent.class, new ScoreComponentSerializer() ).
registerTypeAdapter( SelectorComponent.class, new SelectorComponentSerializer() ).
registerTypeAdapter( ComponentStyle.class, new ComponentStyleSerializer() ).
registerTypeAdapter( Entity.class, new EntitySerializer() ).
registerTypeAdapter( Text.class, new TextSerializer() ).
registerTypeAdapter( Item.class, new ItemSerializer() ).
registerTypeAdapter( ItemTag.class, new ItemTag.Serializer() ).
create();
public static final ThreadLocal<Set<BaseComponent>> serializedComponents = new ThreadLocal<Set<BaseComponent>>();
/**
* Parse a JSON-compliant String as an array of base components. The input
* can be one of either an array of components, or a single component
@ -66,18 +31,7 @@ public class ComponentSerializer implements JsonDeserializer<BaseComponent>
*/
public static BaseComponent[] parse(String json)
{
JsonElement jsonElement = JsonParser.parseString( json );
if ( jsonElement.isJsonArray() )
{
return gson.fromJson( jsonElement, BaseComponent[].class );
} else
{
return new BaseComponent[]
{
gson.fromJson( jsonElement, BaseComponent.class )
};
}
return VersionedComponentSerializer.getDefault().parse( json );
}
/**
@ -90,9 +44,7 @@ public class ComponentSerializer implements JsonDeserializer<BaseComponent>
*/
public static BaseComponent deserialize(String json)
{
JsonElement jsonElement = JsonParser.parseString( json );
return deserialize( jsonElement );
return VersionedComponentSerializer.getDefault().deserialize( json );
}
/**
@ -105,20 +57,7 @@ public class ComponentSerializer implements JsonDeserializer<BaseComponent>
*/
public static BaseComponent deserialize(JsonElement jsonElement)
{
if ( jsonElement instanceof JsonPrimitive )
{
JsonPrimitive primitive = (JsonPrimitive) jsonElement;
if ( primitive.isString() )
{
return new TextComponent( primitive.getAsString() );
}
} else if ( jsonElement instanceof JsonArray )
{
BaseComponent[] array = gson.fromJson( jsonElement, BaseComponent[].class );
return TextComponent.fromArray( array );
}
return gson.fromJson( jsonElement, BaseComponent.class );
return VersionedComponentSerializer.getDefault().deserialize( jsonElement );
}
/**
@ -131,9 +70,7 @@ public class ComponentSerializer implements JsonDeserializer<BaseComponent>
*/
public static ComponentStyle deserializeStyle(String json)
{
JsonElement jsonElement = JsonParser.parseString( json );
return deserializeStyle( jsonElement );
return VersionedComponentSerializer.getDefault().deserializeStyle( json );
}
/**
@ -146,17 +83,17 @@ public class ComponentSerializer implements JsonDeserializer<BaseComponent>
*/
public static ComponentStyle deserializeStyle(JsonElement jsonElement)
{
return gson.fromJson( jsonElement, ComponentStyle.class );
return VersionedComponentSerializer.getDefault().deserializeStyle( jsonElement );
}
public static JsonElement toJson(BaseComponent component)
{
return gson.toJsonTree( component );
return VersionedComponentSerializer.getDefault().toJson( component );
}
public static JsonElement toJson(ComponentStyle style)
{
return gson.toJsonTree( style );
return VersionedComponentSerializer.getDefault().toJson( style );
}
/**
@ -167,7 +104,7 @@ public class ComponentSerializer implements JsonDeserializer<BaseComponent>
@Deprecated
public static String toString(Object object)
{
return gson.toJson( object );
return VersionedComponentSerializer.getDefault().toString( object );
}
/**
@ -178,54 +115,27 @@ public class ComponentSerializer implements JsonDeserializer<BaseComponent>
@Deprecated
public static String toString(Content content)
{
return gson.toJson( content );
return VersionedComponentSerializer.getDefault().toString( content );
}
public static String toString(BaseComponent component)
{
return gson.toJson( component );
return VersionedComponentSerializer.getDefault().toString( component );
}
public static String toString(BaseComponent... components)
{
if ( components.length == 1 )
{
return gson.toJson( components[0] );
} else
{
return gson.toJson( new TextComponent( components ) );
}
return VersionedComponentSerializer.getDefault().toString( components );
}
public static String toString(ComponentStyle style)
{
return gson.toJson( style );
return VersionedComponentSerializer.getDefault().toString( style );
}
@Override
public BaseComponent deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException
{
if ( json.isJsonPrimitive() )
{
return new TextComponent( json.getAsString() );
}
JsonObject object = json.getAsJsonObject();
if ( object.has( "translate" ) )
{
return context.deserialize( json, TranslatableComponent.class );
}
if ( object.has( "keybind" ) )
{
return context.deserialize( json, KeybindComponent.class );
}
if ( object.has( "score" ) )
{
return context.deserialize( json, ScoreComponent.class );
}
if ( object.has( "selector" ) )
{
return context.deserialize( json, SelectorComponent.class );
}
return context.deserialize( json, TextComponent.class );
return VersionedComponentSerializer.getDefault().deserialize( json, typeOfT, context );
}
}

View File

@ -13,6 +13,11 @@ import net.md_5.bungee.api.chat.KeybindComponent;
public class KeybindComponentSerializer extends BaseComponentSerializer implements JsonSerializer<KeybindComponent>, JsonDeserializer<KeybindComponent>
{
public KeybindComponentSerializer(VersionedComponentSerializer serializer)
{
super( serializer );
}
@Override
public KeybindComponent deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException
{

View File

@ -13,6 +13,11 @@ import net.md_5.bungee.api.chat.ScoreComponent;
public class ScoreComponentSerializer extends BaseComponentSerializer implements JsonSerializer<ScoreComponent>, JsonDeserializer<ScoreComponent>
{
public ScoreComponentSerializer(VersionedComponentSerializer serializer)
{
super( serializer );
}
@Override
public ScoreComponent deserialize(JsonElement element, Type type, JsonDeserializationContext context) throws JsonParseException
{

View File

@ -13,6 +13,11 @@ import net.md_5.bungee.api.chat.SelectorComponent;
public class SelectorComponentSerializer extends BaseComponentSerializer implements JsonSerializer<SelectorComponent>, JsonDeserializer<SelectorComponent>
{
public SelectorComponentSerializer(VersionedComponentSerializer serializer)
{
super( serializer );
}
@Override
public SelectorComponent deserialize(JsonElement element, Type type, JsonDeserializationContext context) throws JsonParseException
{
@ -27,7 +32,7 @@ public class SelectorComponentSerializer extends BaseComponentSerializer impleme
JsonElement separator = object.get( "separator" );
if ( separator != null )
{
component.setSeparator( ComponentSerializer.deserialize( separator.getAsString() ) );
component.setSeparator( serializer.deserialize( separator.getAsString() ) );
}
deserialize( object, component, context );
@ -43,7 +48,7 @@ public class SelectorComponentSerializer extends BaseComponentSerializer impleme
if ( component.getSeparator() != null )
{
object.addProperty( "separator", ComponentSerializer.toString( component.getSeparator() ) );
object.addProperty( "separator", serializer.toString( component.getSeparator() ) );
}
return object;
}

View File

@ -13,6 +13,11 @@ import net.md_5.bungee.api.chat.TextComponent;
public class TextComponentSerializer extends BaseComponentSerializer implements JsonSerializer<TextComponent>, JsonDeserializer<TextComponent>
{
public TextComponentSerializer(VersionedComponentSerializer serializer)
{
super( serializer );
}
@Override
public TextComponent deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException
{

View File

@ -15,6 +15,11 @@ import net.md_5.bungee.api.chat.TranslatableComponent;
public class TranslatableComponentSerializer extends BaseComponentSerializer implements JsonSerializer<TranslatableComponent>, JsonDeserializer<TranslatableComponent>
{
public TranslatableComponentSerializer(VersionedComponentSerializer serializer)
{
super( serializer );
}
@Override
public TranslatableComponent deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException
{

View File

@ -0,0 +1,269 @@
package net.md_5.bungee.chat;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonArray;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;
import com.google.gson.JsonParser;
import com.google.gson.JsonPrimitive;
import java.lang.reflect.Type;
import java.util.Set;
import lombok.Getter;
import net.md_5.bungee.api.chat.BaseComponent;
import net.md_5.bungee.api.chat.ComponentStyle;
import net.md_5.bungee.api.chat.ItemTag;
import net.md_5.bungee.api.chat.KeybindComponent;
import net.md_5.bungee.api.chat.ScoreComponent;
import net.md_5.bungee.api.chat.SelectorComponent;
import net.md_5.bungee.api.chat.TextComponent;
import net.md_5.bungee.api.chat.TranslatableComponent;
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.EntitySerializer;
import net.md_5.bungee.api.chat.hover.content.Item;
import net.md_5.bungee.api.chat.hover.content.ItemSerializer;
import net.md_5.bungee.api.chat.hover.content.Text;
import net.md_5.bungee.api.chat.hover.content.TextSerializer;
import org.jetbrains.annotations.ApiStatus;
@ApiStatus.Experimental
public class VersionedComponentSerializer implements JsonDeserializer<BaseComponent>
{
@Getter
@ApiStatus.Internal
private final Gson gson;
@Getter
@ApiStatus.Internal
private final ChatVersion version;
public VersionedComponentSerializer(ChatVersion version)
{
this.version = version;
this.gson = new GsonBuilder().
registerTypeAdapter( BaseComponent.class, this ).
registerTypeAdapter( TextComponent.class, new TextComponentSerializer( this ) ).
registerTypeAdapter( TranslatableComponent.class, new TranslatableComponentSerializer( this ) ).
registerTypeAdapter( KeybindComponent.class, new KeybindComponentSerializer( this ) ).
registerTypeAdapter( ScoreComponent.class, new ScoreComponentSerializer( this ) ).
registerTypeAdapter( SelectorComponent.class, new SelectorComponentSerializer( this ) ).
registerTypeAdapter( ComponentStyle.class, new ComponentStyleSerializer() ).
registerTypeAdapter( Entity.class, new EntitySerializer( this ) ).
registerTypeAdapter( Text.class, new TextSerializer() ).
registerTypeAdapter( Item.class, new ItemSerializer() ).
registerTypeAdapter( ItemTag.class, new ItemTag.Serializer() ).
create();
}
private static final VersionedComponentSerializer v1_16 = new VersionedComponentSerializer( ChatVersion.V1_16 );
private static final VersionedComponentSerializer v1_21_5 = new VersionedComponentSerializer( ChatVersion.V1_21_5 );
public static VersionedComponentSerializer forVersion(ChatVersion version)
{
switch ( version )
{
case V1_16:
return v1_16;
case V1_21_5:
return v1_21_5;
default:
throw new IllegalArgumentException( "Unknown version " + version );
}
}
@Deprecated
@ApiStatus.Internal
public static VersionedComponentSerializer getDefault()
{
return v1_16;
}
@ApiStatus.Internal
public static final ThreadLocal<Set<BaseComponent>> serializedComponents = new ThreadLocal<Set<BaseComponent>>();
/**
* Parse a JSON-compliant String as an array of base components. The input
* can be one of either an array of components, or a single component
* object. If the input is an array, each component will be parsed
* individually and returned in the order that they were parsed. If the
* input is a single component object, a single-valued array with the
* component will be returned.
* <p>
* <strong>NOTE:</strong> {@link #deserialize(String)} is preferred as it
* will parse only one component as opposed to an array of components which
* is non- standard behavior. This method is still appropriate for parsing
* multiple components at once, although such use case is rarely (if at all)
* exhibited in vanilla Minecraft.
*
* @param json the component json to parse
* @return an array of all parsed components
*/
public BaseComponent[] parse(String json)
{
JsonElement jsonElement = JsonParser.parseString( json );
if ( jsonElement.isJsonArray() )
{
return gson.fromJson( jsonElement, BaseComponent[].class );
} else
{
return new BaseComponent[]
{
gson.fromJson( jsonElement, BaseComponent.class )
};
}
}
/**
* Deserialize a JSON-compliant String as a single component.
*
* @param json the component json to parse
* @return the deserialized component
* @throws IllegalArgumentException if anything other than a valid JSON
* component string is passed as input
*/
public BaseComponent deserialize(String json)
{
JsonElement jsonElement = JsonParser.parseString( json );
return deserialize( jsonElement );
}
/**
* Deserialize a JSON element as a single component.
*
* @param jsonElement the component json to parse
* @return the deserialized component
* @throws IllegalArgumentException if anything other than a valid JSON
* component is passed as input
*/
public BaseComponent deserialize(JsonElement jsonElement)
{
if ( jsonElement instanceof JsonPrimitive )
{
JsonPrimitive primitive = (JsonPrimitive) jsonElement;
if ( primitive.isString() )
{
return new TextComponent( primitive.getAsString() );
}
} else if ( jsonElement instanceof JsonArray )
{
BaseComponent[] array = gson.fromJson( jsonElement, BaseComponent[].class );
return TextComponent.fromArray( array );
}
return gson.fromJson( jsonElement, BaseComponent.class );
}
/**
* Deserialize a JSON-compliant String as a component style.
*
* @param json the component style json to parse
* @return the deserialized component style
* @throws IllegalArgumentException if anything other than a valid JSON
* component style string is passed as input
*/
public ComponentStyle deserializeStyle(String json)
{
JsonElement jsonElement = JsonParser.parseString( json );
return deserializeStyle( jsonElement );
}
/**
* Deserialize a JSON element as a component style.
*
* @param jsonElement the component style json to parse
* @return the deserialized component style
* @throws IllegalArgumentException if anything other than a valid JSON
* component style is passed as input
*/
public ComponentStyle deserializeStyle(JsonElement jsonElement)
{
return gson.fromJson( jsonElement, ComponentStyle.class );
}
public JsonElement toJson(BaseComponent component)
{
return gson.toJsonTree( component );
}
public JsonElement toJson(ComponentStyle style)
{
return gson.toJsonTree( style );
}
/**
* @param object the object to serialize
* @return the JSON string representation of the object
* @deprecated Error-prone, be careful which object you input here
*/
@Deprecated
public String toString(Object object)
{
return gson.toJson( object );
}
/**
* @param content the content to serialize
* @return the JSON string representation of the object
* @deprecated for legacy internal use only
*/
@Deprecated
public String toString(Content content)
{
return gson.toJson( content );
}
public String toString(BaseComponent component)
{
return gson.toJson( component );
}
public String toString(BaseComponent... components)
{
if ( components.length == 1 )
{
return gson.toJson( components[0] );
} else
{
return gson.toJson( new TextComponent( components ) );
}
}
public String toString(ComponentStyle style)
{
return gson.toJson( style );
}
@Override
public BaseComponent deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException
{
if ( json.isJsonPrimitive() )
{
return new TextComponent( json.getAsString() );
}
JsonObject object = json.getAsJsonObject();
if ( object.has( "translate" ) )
{
return context.deserialize( json, TranslatableComponent.class );
}
if ( object.has( "keybind" ) )
{
return context.deserialize( json, KeybindComponent.class );
}
if ( object.has( "score" ) )
{
return context.deserialize( json, ScoreComponent.class );
}
if ( object.has( "selector" ) )
{
return context.deserialize( json, SelectorComponent.class );
}
return context.deserialize( json, TextComponent.class );
}
}

View File

@ -4,15 +4,14 @@
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>net.md-5</groupId>
<groupId>fr.pandacube.bungeecord</groupId>
<artifactId>bungeecord-parent</artifactId>
<version>1.21-R0.1-SNAPSHOT</version>
<version>1.21-R0.3-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.3-SNAPSHOT</version>
<packaging>jar</packaging>
<name>BungeeCord-Config</name>

View File

@ -4,15 +4,14 @@
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>net.md-5</groupId>
<groupId>fr.pandacube.bungeecord</groupId>
<artifactId>bungeecord-parent</artifactId>
<version>1.21-R0.1-SNAPSHOT</version>
<version>1.21-R0.3-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.3-SNAPSHOT</version>
<packaging>jar</packaging>
<name>BungeeCord-Event</name>

View File

@ -4,15 +4,14 @@
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>net.md-5</groupId>
<groupId>fr.pandacube.bungeecord</groupId>
<artifactId>bungeecord-parent</artifactId>
<version>1.21-R0.1-SNAPSHOT</version>
<version>1.21-R0.3-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.3-SNAPSHOT</version>
<packaging>jar</packaging>
<name>BungeeCord-Log</name>
@ -26,7 +25,7 @@
<scope>compile</scope>
</dependency>
<dependency>
<groupId>net.md-5</groupId>
<groupId>fr.pandacube.bungeecord</groupId>
<artifactId>bungeecord-chat</artifactId>
<version>${project.version}</version>
<scope>compile</scope>

View File

@ -1,31 +0,0 @@
<?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>

View File

@ -1,20 +0,0 @@
<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-module</artifactId>
<version>1.21-R0.1-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>
<packaging>jar</packaging>
<name>cmd_alert</name>
<description>Provides the alert and alertraw commands</description>
</project>

View File

@ -1,47 +0,0 @@
package net.md_5.bungee.module.cmd.alert;
import java.util.Locale;
import net.md_5.bungee.api.ChatColor;
import net.md_5.bungee.api.CommandSender;
import net.md_5.bungee.api.ProxyServer;
import net.md_5.bungee.api.chat.TextComponent;
import net.md_5.bungee.api.plugin.Command;
public class CommandAlert extends Command
{
public CommandAlert()
{
super( "alert", "bungeecord.command.alert" );
}
@Override
public void execute(CommandSender sender, String[] args)
{
if ( args.length == 0 )
{
sender.sendMessage( ProxyServer.getInstance().getTranslation( "message_needed" ) );
} else
{
StringBuilder builder = new StringBuilder();
if ( args[0].toLowerCase( Locale.ROOT ).startsWith( "&h" ) )
{
// Remove &h
args[0] = args[0].substring( 2 );
} else
{
builder.append( ProxyServer.getInstance().getTranslation( "alert" ) );
}
for ( String s : args )
{
builder.append( ChatColor.translateAlternateColorCodes( '&', s ) );
builder.append( " " );
}
String message = builder.substring( 0, builder.length() - 1 );
ProxyServer.getInstance().broadcast( TextComponent.fromLegacy( message ) );
}
}
}

View File

@ -1,56 +0,0 @@
package net.md_5.bungee.module.cmd.alert;
import com.google.common.base.Joiner;
import net.md_5.bungee.api.ChatColor;
import net.md_5.bungee.api.CommandSender;
import net.md_5.bungee.api.ProxyServer;
import net.md_5.bungee.api.chat.ComponentBuilder;
import net.md_5.bungee.api.chat.HoverEvent;
import net.md_5.bungee.api.connection.ProxiedPlayer;
import net.md_5.bungee.api.plugin.Command;
import net.md_5.bungee.chat.ComponentSerializer;
public class CommandAlertRaw extends Command
{
public CommandAlertRaw()
{
super( "alertraw", "bungeecord.command.alert" );
}
@Override
public void execute(CommandSender sender, String[] args)
{
if ( args.length == 0 )
{
sender.sendMessage( ProxyServer.getInstance().getTranslation( "message_needed" ) );
} else
{
String message = Joiner.on( ' ' ).join( args );
try
{
ProxyServer.getInstance().broadcast( ComponentSerializer.parse( message ) );
} catch ( Exception e )
{
Throwable error = e;
while ( error.getCause() != null )
{
error = error.getCause();
}
if ( sender instanceof ProxiedPlayer )
{
sender.sendMessage( new ComponentBuilder( ProxyServer.getInstance().getTranslation( "error_occurred_player" ) )
.event( new HoverEvent( HoverEvent.Action.SHOW_TEXT, new ComponentBuilder( error.getMessage() )
.color( ChatColor.RED )
.create() ) )
.create()
);
} else
{
sender.sendMessage( ProxyServer.getInstance().getTranslation( "error_occurred_console", error.getMessage() ) );
}
}
}
}
}

View File

@ -1,14 +0,0 @@
package net.md_5.bungee.module.cmd.alert;
import net.md_5.bungee.api.plugin.Plugin;
public class PluginAlert extends Plugin
{
@Override
public void onEnable()
{
getProxy().getPluginManager().registerCommand( this, new CommandAlert() );
getProxy().getPluginManager().registerCommand( this, new CommandAlertRaw() );
}
}

View File

@ -1,5 +0,0 @@
name: ${project.name}
main: net.md_5.bungee.module.cmd.alert.PluginAlert
version: ${describe}
description: ${project.description}
author: ${module.author}

View File

@ -1,31 +0,0 @@
<?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>

View File

@ -1,20 +0,0 @@
<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-module</artifactId>
<version>1.21-R0.1-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>
<packaging>jar</packaging>
<name>cmd_find</name>
<description>Provides the find command</description>
</project>

View File

@ -1,58 +0,0 @@
package net.md_5.bungee.module.cmd.find;
import java.util.Collections;
import net.md_5.bungee.api.CommandSender;
import net.md_5.bungee.api.ProxyServer;
import net.md_5.bungee.api.chat.ClickEvent;
import net.md_5.bungee.api.chat.ComponentBuilder;
import net.md_5.bungee.api.chat.HoverEvent;
import net.md_5.bungee.api.config.ServerInfo;
import net.md_5.bungee.api.connection.ProxiedPlayer;
import net.md_5.bungee.command.PlayerCommand;
public class CommandFind extends PlayerCommand
{
public CommandFind()
{
super( "find", "bungeecord.command.find" );
}
@Override
public void execute(CommandSender sender, String[] args)
{
if ( args.length != 1 )
{
sender.sendMessage( ProxyServer.getInstance().getTranslation( "username_needed" ) );
} else
{
ProxiedPlayer player = ProxyServer.getInstance().getPlayer( args[0] );
if ( player == null || player.getServer() == null )
{
sender.sendMessage( ProxyServer.getInstance().getTranslation( "user_not_online" ) );
} else
{
boolean moduleLoaded = ProxyServer.getInstance().getPluginManager().getPlugin( "cmd_server" ) != null;
ServerInfo server = player.getServer().getInfo();
ComponentBuilder componentBuilder = new ComponentBuilder().appendLegacy( ProxyServer.getInstance().getTranslation( "user_online_at", player.getName(), server.getName() ) );
if ( moduleLoaded && server.canAccess( sender ) )
{
componentBuilder.event( new HoverEvent(
HoverEvent.Action.SHOW_TEXT,
new ComponentBuilder().appendLegacy( ProxyServer.getInstance().getTranslation( "click_to_connect" ) ).create() )
);
componentBuilder.event( new ClickEvent( ClickEvent.Action.RUN_COMMAND, "/server " + server.getName() ) );
}
sender.sendMessage( componentBuilder.create() );
}
}
}
@Override
public Iterable<String> onTabComplete(CommandSender sender, String[] args)
{
return args.length == 1 ? super.onTabComplete( sender, args ) : Collections.emptyList();
}
}

View File

@ -1,13 +0,0 @@
package net.md_5.bungee.module.cmd.find;
import net.md_5.bungee.api.plugin.Plugin;
public class PluginFind extends Plugin
{
@Override
public void onEnable()
{
getProxy().getPluginManager().registerCommand( this, new CommandFind() );
}
}

View File

@ -1,5 +0,0 @@
name: ${project.name}
main: net.md_5.bungee.module.cmd.find.PluginFind
version: ${describe}
description: ${project.description}
author: ${module.author}

View File

@ -1,31 +0,0 @@
<?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>

View File

@ -1,20 +0,0 @@
<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-module</artifactId>
<version>1.21-R0.1-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>
<packaging>jar</packaging>
<name>cmd_kick</name>
<description>Provides the gkick command</description>
</project>

View File

@ -1,72 +0,0 @@
package net.md_5.bungee.module.cmd.kick;
import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableSet;
import java.util.HashSet;
import java.util.Locale;
import java.util.Set;
import net.md_5.bungee.api.ChatColor;
import net.md_5.bungee.api.CommandSender;
import net.md_5.bungee.api.ProxyServer;
import net.md_5.bungee.api.chat.TextComponent;
import net.md_5.bungee.api.connection.ProxiedPlayer;
import net.md_5.bungee.api.plugin.Command;
import net.md_5.bungee.api.plugin.TabExecutor;
public class CommandKick extends Command implements TabExecutor
{
public CommandKick()
{
super( "gkick", "bungeecord.command.kick" );
}
@Override
public void execute(CommandSender sender, String[] args)
{
if ( args.length == 0 )
{
sender.sendMessage( ProxyServer.getInstance().getTranslation( "username_needed" ) );
} else
{
ProxiedPlayer player = ProxyServer.getInstance().getPlayer( args[0] );
if ( player == null )
{
sender.sendMessage( TextComponent.fromLegacy( ProxyServer.getInstance().getTranslation( "user_not_online" ) ) );
return;
}
if ( args.length == 1 )
{
player.disconnect( TextComponent.fromLegacy( ProxyServer.getInstance().getTranslation( "kick_message" ) ) );
} else
{
String[] reason = new String[ args.length - 1 ];
System.arraycopy( args, 1, reason, 0, reason.length );
player.disconnect( TextComponent.fromLegacy( ChatColor.translateAlternateColorCodes( '&', Joiner.on( ' ' ).join( reason ) ) ) );
}
}
}
@Override
public Iterable<String> onTabComplete(CommandSender sender, String[] args)
{
if ( args.length == 1 )
{
Set<String> matches = new HashSet<>();
String search = args[0].toLowerCase( Locale.ROOT );
for ( ProxiedPlayer player : ProxyServer.getInstance().getPlayers() )
{
if ( player.getName().toLowerCase( Locale.ROOT ).startsWith( search ) )
{
matches.add( player.getName() );
}
}
return matches;
} else
{
return ImmutableSet.of();
}
}
}

View File

@ -1,13 +0,0 @@
package net.md_5.bungee.module.cmd.kick;
import net.md_5.bungee.api.plugin.Plugin;
public class PluginKick extends Plugin
{
@Override
public void onEnable()
{
getProxy().getPluginManager().registerCommand( this, new CommandKick() );
}
}

View File

@ -1,5 +0,0 @@
name: ${project.name}
main: net.md_5.bungee.module.cmd.kick.PluginKick
version: ${describe}
description: ${project.description}
author: ${module.author}

View File

@ -1,31 +0,0 @@
<?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>

View File

@ -1,20 +0,0 @@
<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-module</artifactId>
<version>1.21-R0.1-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>
<packaging>jar</packaging>
<name>cmd_list</name>
<description>Provides the glist command</description>
</project>

View File

@ -1,79 +0,0 @@
package net.md_5.bungee.module.cmd.list;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import net.md_5.bungee.api.ChatColor;
import net.md_5.bungee.api.CommandSender;
import net.md_5.bungee.api.ProxyServer;
import net.md_5.bungee.api.chat.BaseComponent;
import net.md_5.bungee.api.chat.ClickEvent;
import net.md_5.bungee.api.chat.ComponentBuilder;
import net.md_5.bungee.api.chat.HoverEvent;
import net.md_5.bungee.api.chat.hover.content.Text;
import net.md_5.bungee.api.config.ServerInfo;
import net.md_5.bungee.api.connection.ProxiedPlayer;
import net.md_5.bungee.api.plugin.Command;
import net.md_5.bungee.api.plugin.TabExecutor;
/**
* Command to list all players connected to the proxy.
*/
public class CommandList extends Command implements TabExecutor
{
public CommandList()
{
super( "glist", "bungeecord.command.list" );
}
@Override
public void execute(CommandSender sender, String[] args)
{
boolean hideEmptyServers = ( args.length == 0 ) || !args[0].equalsIgnoreCase( "all" );
boolean moduleLoaded = ProxyServer.getInstance().getPluginManager().getPlugin( "cmd_server" ) != null;
for ( ServerInfo server : ProxyServer.getInstance().getServers().values() )
{
if ( !server.canAccess( sender ) )
{
continue;
}
Collection<ProxiedPlayer> serverPlayers = server.getPlayers();
if ( hideEmptyServers && serverPlayers.isEmpty() )
{
continue;
}
List<String> players = new ArrayList<>();
for ( ProxiedPlayer player : serverPlayers )
{
players.add( player.getDisplayName() );
}
Collections.sort( players, String.CASE_INSENSITIVE_ORDER );
BaseComponent baseComponent = new ComponentBuilder().appendLegacy( ProxyServer.getInstance().getTranslation( "command_list", server.getName(), players.size(), String.join( ChatColor.RESET + ", ", players ) ) ).build();
if ( moduleLoaded )
{
baseComponent.setHoverEvent( new HoverEvent(
HoverEvent.Action.SHOW_TEXT,
new Text( new ComponentBuilder().appendLegacy( ProxyServer.getInstance().getTranslation( "click_to_connect" ) ).create() ) )
);
baseComponent.setClickEvent( new ClickEvent( ClickEvent.Action.RUN_COMMAND, "/server " + server.getName() ) );
}
sender.sendMessage( baseComponent );
}
sender.sendMessage( ProxyServer.getInstance().getTranslation( "total_players", ProxyServer.getInstance().getOnlineCount() ) );
}
@Override
public Iterable<String> onTabComplete(CommandSender sender, String[] args)
{
return ( args.length > 1 ) ? Collections.emptyList() : Collections.singletonList( "all" );
}
}

View File

@ -1,13 +0,0 @@
package net.md_5.bungee.module.cmd.list;
import net.md_5.bungee.api.plugin.Plugin;
public class PluginList extends Plugin
{
@Override
public void onEnable()
{
getProxy().getPluginManager().registerCommand( this, new CommandList() );
}
}

View File

@ -1,5 +0,0 @@
name: ${project.name}
main: net.md_5.bungee.module.cmd.list.PluginList
version: ${describe}
description: ${project.description}
author: ${module.author}

View File

@ -1,31 +0,0 @@
<?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>

View File

@ -1,20 +0,0 @@
<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-module</artifactId>
<version>1.21-R0.1-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>
<packaging>jar</packaging>
<name>cmd_send</name>
<description>Provides the gsend command</description>
</project>

View File

@ -1,200 +0,0 @@
package net.md_5.bungee.module.cmd.send;
import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableSet;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import net.md_5.bungee.api.Callback;
import net.md_5.bungee.api.ChatColor;
import net.md_5.bungee.api.CommandSender;
import net.md_5.bungee.api.ProxyServer;
import net.md_5.bungee.api.ServerConnectRequest;
import net.md_5.bungee.api.chat.ComponentBuilder;
import net.md_5.bungee.api.chat.HoverEvent;
import net.md_5.bungee.api.config.ServerInfo;
import net.md_5.bungee.api.connection.ProxiedPlayer;
import net.md_5.bungee.api.event.ServerConnectEvent;
import net.md_5.bungee.api.plugin.Command;
import net.md_5.bungee.api.plugin.TabExecutor;
public class CommandSend extends Command implements TabExecutor
{
protected static class SendCallback
{
private final Map<ServerConnectRequest.Result, List<String>> results = new HashMap<>();
private final CommandSender sender;
private int count = 0;
public SendCallback(CommandSender sender)
{
this.sender = sender;
for ( ServerConnectRequest.Result result : ServerConnectRequest.Result.values() )
{
results.put( result, Collections.synchronizedList( new ArrayList<>() ) );
}
}
public void lastEntryDone()
{
sender.sendMessage( ChatColor.GREEN.toString() + ChatColor.BOLD + "Send Results:" );
for ( Map.Entry<ServerConnectRequest.Result, List<String>> entry : results.entrySet() )
{
ComponentBuilder builder = new ComponentBuilder( "" );
if ( !entry.getValue().isEmpty() )
{
builder.event( new HoverEvent( HoverEvent.Action.SHOW_TEXT,
new ComponentBuilder( Joiner.on( ", " ).join( entry.getValue() ) ).color( ChatColor.YELLOW ).create() ) );
}
builder.append( entry.getKey().name() + ": " ).color( ChatColor.GREEN );
builder.append( "" + entry.getValue().size() ).bold( true );
sender.sendMessage( builder.create() );
}
}
public static class Entry implements Callback<ServerConnectRequest.Result>
{
private final SendCallback callback;
private final ProxiedPlayer player;
private final ServerInfo target;
public Entry(SendCallback callback, ProxiedPlayer player, ServerInfo target)
{
this.callback = callback;
this.player = player;
this.target = target;
this.callback.count++;
}
@Override
public void done(ServerConnectRequest.Result result, Throwable error)
{
callback.results.get( result ).add( player.getName() );
if ( result == ServerConnectRequest.Result.SUCCESS )
{
player.sendMessage( ProxyServer.getInstance().getTranslation( "you_got_summoned", target.getName(), callback.sender.getName() ) );
}
if ( --callback.count == 0 )
{
callback.lastEntryDone();
}
}
}
}
public CommandSend()
{
super( "send", "bungeecord.command.send" );
}
@Override
public void execute(CommandSender sender, String[] args)
{
if ( args.length != 2 )
{
sender.sendMessage( ProxyServer.getInstance().getTranslation( "send_cmd_usage" ) );
return;
}
ServerInfo server = ProxyServer.getInstance().getServerInfo( args[1] );
if ( server == null )
{
sender.sendMessage( ProxyServer.getInstance().getTranslation( "no_server" ) );
return;
}
List<ProxiedPlayer> targets;
if ( args[0].equalsIgnoreCase( "all" ) )
{
targets = new ArrayList<>( ProxyServer.getInstance().getPlayers() );
} else if ( args[0].equalsIgnoreCase( "current" ) )
{
if ( !( sender instanceof ProxiedPlayer ) )
{
sender.sendMessage( ProxyServer.getInstance().getTranslation( "player_only" ) );
return;
}
ProxiedPlayer player = (ProxiedPlayer) sender;
targets = new ArrayList<>( player.getServer().getInfo().getPlayers() );
} else
{
// If we use a server name, send the entire server. This takes priority over players.
ServerInfo serverTarget = ProxyServer.getInstance().getServerInfo( args[0] );
if ( serverTarget != null )
{
targets = new ArrayList<>( serverTarget.getPlayers() );
} else
{
ProxiedPlayer player = ProxyServer.getInstance().getPlayer( args[0] );
if ( player == null )
{
sender.sendMessage( ProxyServer.getInstance().getTranslation( "user_not_online" ) );
return;
}
targets = Collections.singletonList( player );
}
}
final SendCallback callback = new SendCallback( sender );
for ( ProxiedPlayer player : targets )
{
ServerConnectRequest request = ServerConnectRequest.builder()
.target( server )
.reason( ServerConnectEvent.Reason.COMMAND )
.callback( new SendCallback.Entry( callback, player, server ) )
.build();
player.connect( request );
}
sender.sendMessage( ChatColor.DARK_GREEN + "Attempting to send " + targets.size() + " players to " + server.getName() );
}
@Override
public Iterable<String> onTabComplete(CommandSender sender, String[] args)
{
if ( args.length > 2 || args.length == 0 )
{
return ImmutableSet.of();
}
Set<String> matches = new HashSet<>();
if ( args.length == 1 )
{
String search = args[0].toLowerCase( Locale.ROOT );
for ( ProxiedPlayer player : ProxyServer.getInstance().getPlayers() )
{
if ( player.getName().toLowerCase( Locale.ROOT ).startsWith( search ) )
{
matches.add( player.getName() );
}
}
if ( "all".startsWith( search ) )
{
matches.add( "all" );
}
if ( "current".startsWith( search ) )
{
matches.add( "current" );
}
} else
{
String search = args[1].toLowerCase( Locale.ROOT );
for ( String server : ProxyServer.getInstance().getServers().keySet() )
{
if ( server.toLowerCase( Locale.ROOT ).startsWith( search ) )
{
matches.add( server );
}
}
}
return matches;
}
}

View File

@ -1,13 +0,0 @@
package net.md_5.bungee.module.cmd.send;
import net.md_5.bungee.api.plugin.Plugin;
public class PluginSend extends Plugin
{
@Override
public void onEnable()
{
getProxy().getPluginManager().registerCommand( this, new CommandSend() );
}
}

View File

@ -1,5 +0,0 @@
name: ${project.name}
main: net.md_5.bungee.module.cmd.send.PluginSend
version: ${describe}
description: ${project.description}
author: ${module.author}

View File

@ -1,31 +0,0 @@
<?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>

View File

@ -1,20 +0,0 @@
<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-module</artifactId>
<version>1.21-R0.1-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>
<packaging>jar</packaging>
<name>cmd_server</name>
<description>Provides the server command</description>
</project>

View File

@ -1,104 +0,0 @@
package net.md_5.bungee.module.cmd.server;
import com.google.common.base.Function;
import com.google.common.base.Predicate;
import com.google.common.collect.Iterables;
import java.util.Collections;
import java.util.Locale;
import java.util.Map;
import net.md_5.bungee.api.CommandSender;
import net.md_5.bungee.api.ProxyServer;
import net.md_5.bungee.api.chat.ClickEvent;
import net.md_5.bungee.api.chat.ComponentBuilder;
import net.md_5.bungee.api.chat.HoverEvent;
import net.md_5.bungee.api.chat.TextComponent;
import net.md_5.bungee.api.config.ServerInfo;
import net.md_5.bungee.api.connection.ProxiedPlayer;
import net.md_5.bungee.api.event.ServerConnectEvent;
import net.md_5.bungee.api.plugin.Command;
import net.md_5.bungee.api.plugin.TabExecutor;
/**
* Command to list and switch a player between available servers.
*/
public class CommandServer extends Command implements TabExecutor
{
public CommandServer()
{
super( "server", "bungeecord.command.server" );
}
@Override
public void execute(CommandSender sender, String[] args)
{
Map<String, ServerInfo> servers = ProxyServer.getInstance().getServers();
if ( args.length == 0 )
{
if ( sender instanceof ProxiedPlayer )
{
sender.sendMessage( ProxyServer.getInstance().getTranslation( "current_server", ( (ProxiedPlayer) sender ).getServer().getInfo().getName() ) );
}
ComponentBuilder serverList = new ComponentBuilder().appendLegacy( ProxyServer.getInstance().getTranslation( "server_list" ) );
boolean first = true;
for ( ServerInfo server : servers.values() )
{
if ( server.canAccess( sender ) )
{
TextComponent serverTextComponent = new TextComponent( first ? server.getName() : ", " + server.getName() );
int count = server.getPlayers().size();
serverTextComponent.setHoverEvent( new HoverEvent(
HoverEvent.Action.SHOW_TEXT,
new ComponentBuilder( count + ( count == 1 ? " player" : " players" ) + "\n" ).appendLegacy( ProxyServer.getInstance().getTranslation( "click_to_connect" ) ).create() )
);
serverTextComponent.setClickEvent( new ClickEvent( ClickEvent.Action.RUN_COMMAND, "/server " + server.getName() ) );
serverList.append( serverTextComponent );
first = false;
}
}
sender.sendMessage( serverList.create() );
} else
{
if ( !( sender instanceof ProxiedPlayer ) )
{
return;
}
ProxiedPlayer player = (ProxiedPlayer) sender;
ServerInfo server = servers.get( args[0] );
if ( server == null )
{
player.sendMessage( ProxyServer.getInstance().getTranslation( "no_server" ) );
} else if ( !server.canAccess( player ) )
{
player.sendMessage( ProxyServer.getInstance().getTranslation( "no_server_permission" ) );
} else
{
player.connect( server, ServerConnectEvent.Reason.COMMAND );
}
}
}
@Override
public Iterable<String> onTabComplete(final CommandSender sender, final String[] args)
{
return ( args.length > 1 ) ? Collections.EMPTY_LIST : Iterables.transform( Iterables.filter( ProxyServer.getInstance().getServers().values(), new Predicate<ServerInfo>()
{
private final String lower = ( args.length == 0 ) ? "" : args[0].toLowerCase( Locale.ROOT );
@Override
public boolean apply(ServerInfo input)
{
return input.getName().toLowerCase( Locale.ROOT ).startsWith( lower ) && input.canAccess( sender );
}
} ), new Function<ServerInfo, String>()
{
@Override
public String apply(ServerInfo input)
{
return input.getName();
}
} );
}
}

View File

@ -1,13 +0,0 @@
package net.md_5.bungee.module.cmd.server;
import net.md_5.bungee.api.plugin.Plugin;
public class PluginServer extends Plugin
{
@Override
public void onEnable()
{
getProxy().getPluginManager().registerCommand( this, new CommandServer() );
}
}

View File

@ -1,5 +0,0 @@
name: ${project.name}
main: net.md_5.bungee.module.cmd.server.PluginServer
version: ${describe}
description: ${project.description}
author: ${module.author}

View File

@ -1,55 +0,0 @@
<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.1-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<groupId>net.md-5</groupId>
<artifactId>bungeecord-module</artifactId>
<version>1.21-R0.1-SNAPSHOT</version>
<packaging>pom</packaging>
<name>BungeeCord Modules</name>
<description>Parent project for all BungeeCord modules.</description>
<modules>
<module>cmd-alert</module>
<module>cmd-find</module>
<module>cmd-kick</module>
<module>cmd-list</module>
<module>cmd-send</module>
<module>cmd-server</module>
<module>reconnect-yaml</module>
</modules>
<properties>
<module.author>SpigotMC</module.author>
<maven.deploy.skip>true</maven.deploy.skip>
<maven.javadoc.skip>true</maven.javadoc.skip>
</properties>
<dependencies>
<dependency>
<groupId>net.md-5</groupId>
<artifactId>bungeecord-api</artifactId>
<version>${project.version}</version>
<scope>compile</scope>
</dependency>
</dependencies>
<build>
<finalName>${project.name}</finalName>
<resources>
<resource>
<filtering>true</filtering>
<directory>${basedir}/src/main/resources</directory>
</resource>
</resources>
</build>
</project>

View File

@ -1,31 +0,0 @@
<?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>

View File

@ -1,20 +0,0 @@
<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-module</artifactId>
<version>1.21-R0.1-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>
<packaging>jar</packaging>
<name>reconnect_yaml</name>
<description>Provides reconnect location functionality in locations.yml</description>
</project>

View File

@ -1,22 +0,0 @@
package net.md_5.bungee.module.reconnect.yaml;
import net.md_5.bungee.api.config.ListenerInfo;
import net.md_5.bungee.api.plugin.Plugin;
public class PluginYaml extends Plugin
{
@Override
public void onEnable()
{
// TODO: Abstract this for other reconnect modules
for ( ListenerInfo info : getProxy().getConfig().getListeners() )
{
if ( !info.isForceDefault() && getProxy().getReconnectHandler() == null )
{
getProxy().setReconnectHandler( new YamlReconnectHandler() );
break;
}
}
}
}

View File

@ -1,115 +0,0 @@
package net.md_5.bungee.module.reconnect.yaml;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.logging.Level;
import net.md_5.bungee.api.AbstractReconnectHandler;
import net.md_5.bungee.api.ProxyServer;
import net.md_5.bungee.api.config.ServerInfo;
import net.md_5.bungee.api.connection.ProxiedPlayer;
import net.md_5.bungee.util.CaseInsensitiveMap;
import org.yaml.snakeyaml.Yaml;
public class YamlReconnectHandler extends AbstractReconnectHandler
{
private final Yaml yaml = new Yaml();
private final File file = new File( "locations.yml" );
private final ReadWriteLock lock = new ReentrantReadWriteLock();
/*========================================================================*/
private CaseInsensitiveMap<String> data;
@SuppressWarnings("unchecked")
public YamlReconnectHandler()
{
try
{
file.createNewFile();
try ( FileReader rd = new FileReader( file ) )
{
Map map = yaml.loadAs( rd, Map.class );
if ( map != null )
{
data = new CaseInsensitiveMap<>( map );
}
}
} catch ( Exception ex )
{
file.renameTo( new File( "locations.yml.old" ) );
ProxyServer.getInstance().getLogger().log( Level.WARNING, "Could not load reconnect locations, resetting them" );
}
if ( data == null )
{
data = new CaseInsensitiveMap<>();
}
}
@Override
protected ServerInfo getStoredServer(ProxiedPlayer player)
{
ServerInfo server = null;
lock.readLock().lock();
try
{
server = ProxyServer.getInstance().getServerInfo( data.get( key( player ) ) );
} finally
{
lock.readLock().unlock();
}
return server;
}
@Override
public void setServer(ProxiedPlayer player)
{
lock.writeLock().lock();
try
{
data.put( key( player ), ( player.getReconnectServer() != null ) ? player.getReconnectServer().getName() : player.getServer().getInfo().getName() );
} finally
{
lock.writeLock().unlock();
}
}
private String key(ProxiedPlayer player)
{
InetSocketAddress host = player.getPendingConnection().getVirtualHost();
return player.getName() + ";" + host.getHostString() + ":" + host.getPort();
}
@Override
public void save()
{
Map<String, String> copy = new HashMap<>();
lock.readLock().lock();
try
{
copy.putAll( data );
} finally
{
lock.readLock().unlock();
}
try ( FileWriter wr = new FileWriter( file ) )
{
yaml.dump( copy, wr );
} catch ( IOException ex )
{
ProxyServer.getInstance().getLogger().log( Level.WARNING, "Could not save reconnect locations", ex );
}
}
@Override
public void close()
{
}
}

View File

@ -1,5 +0,0 @@
name: ${project.name}
main: net.md_5.bungee.module.reconnect.yaml.PluginYaml
version: ${describe}
description: ${project.description}
author: ${module.author}

View File

@ -4,15 +4,14 @@
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>net.md-5</groupId>
<groupId>fr.pandacube.bungeecord</groupId>
<artifactId>bungeecord-parent</artifactId>
<version>1.21-R0.1-SNAPSHOT</version>
<version>1.21-R0.3-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.3-SNAPSHOT</version>
<packaging>jar</packaging>
<name>BungeeCord-Native</name>

View File

@ -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

View File

@ -48,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() ) )
{
if ( compress )
{
out.ensureWritable( OUTPUT_BUFFER_SIZE );
} else
{
Preconditions.checkArgument( out.isWritable(), "Output buffer overrun" );
}
int processed;
try

View File

@ -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 );

22
pom.xml
View File

@ -3,9 +3,9 @@
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>
<groupId>net.md-5</groupId>
<groupId>fr.pandacube.bungeecord</groupId>
<artifactId>bungeecord-parent</artifactId>
<version>1.21-R0.1-SNAPSHOT</version>
<version>1.21-R0.3-SNAPSHOT</version>
<packaging>pom</packaging>
<name>BungeeCord-Parent</name>
@ -37,7 +37,6 @@
<module>config</module>
<module>event</module>
<module>log</module>
<module>module</module>
<module>protocol</module>
<module>proxy</module>
<module>query</module>
@ -72,7 +71,7 @@
<properties>
<build.number>unknown</build.number>
<lombok.version>1.18.32</lombok.version>
<lombok.version>1.18.36</lombok.version>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
@ -83,7 +82,7 @@
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-bom</artifactId>
<version>4.1.115.Final</version>
<version>4.1.119.Final</version>
<type>pom</type>
<scope>import</scope>
</dependency>
@ -138,7 +137,7 @@
<artifactId>scriptus</artifactId>
<version>0.5.0</version>
<configuration>
<format>git:${project.name}:${project.version}:%s:${build.number}</format>
<format>git:${project.name}-Pandacube:${project.version}:%s:${build.number}</format>
</configuration>
<executions>
<execution>
@ -157,7 +156,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-checkstyle-plugin</artifactId>
<version>3.3.1</version>
<version>3.6.0</version>
<executions>
<execution>
<phase>process-classes</phase>
@ -275,6 +274,15 @@
</pluginManagement>
</build>
</profile>
<profile>
<id>jdk-21-proc</id>
<activation>
<jdk>[21,)</jdk>
</activation>
<properties>
<maven.compiler.proc>full</maven.compiler.proc>
</properties>
</profile>
<profile>
<id>dist</id>
<build>

View File

@ -4,15 +4,14 @@
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>net.md-5</groupId>
<groupId>fr.pandacube.bungeecord</groupId>
<artifactId>bungeecord-parent</artifactId>
<version>1.21-R0.1-SNAPSHOT</version>
<version>1.21-R0.3-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<groupId>net.md-5</groupId>
<artifactId>bungeecord-protocol</artifactId>
<version>1.21-R0.1-SNAPSHOT</version>
<version>1.21-R0.3-SNAPSHOT</version>
<packaging>jar</packaging>
<name>BungeeCord-Protocol</name>
@ -35,7 +34,7 @@
<scope>compile</scope>
</dependency>
<dependency>
<groupId>net.md-5</groupId>
<groupId>fr.pandacube.bungeecord</groupId>
<artifactId>bungeecord-chat</artifactId>
<version>${project.version}</version>
<scope>compile</scope>

View File

@ -0,0 +1,19 @@
package net.md_5.bungee.protocol;
import net.md_5.bungee.chat.ChatVersion;
import net.md_5.bungee.chat.VersionedComponentSerializer;
public class ChatSerializer
{
public static VersionedComponentSerializer forVersion(int protocolVersion)
{
if ( protocolVersion >= ProtocolConstants.MINECRAFT_1_21_5 )
{
return VersionedComponentSerializer.forVersion( ChatVersion.V1_21_5 );
} else
{
return VersionedComponentSerializer.forVersion( ChatVersion.V1_16 );
}
}
}

View File

@ -15,12 +15,12 @@ import java.util.Arrays;
import java.util.BitSet;
import java.util.EnumSet;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.function.BiConsumer;
import lombok.RequiredArgsConstructor;
import net.md_5.bungee.api.chat.BaseComponent;
import net.md_5.bungee.api.chat.ComponentStyle;
import net.md_5.bungee.chat.ComponentSerializer;
import se.llbit.nbt.ErrorTag;
import se.llbit.nbt.NamedTag;
import se.llbit.nbt.SpecificTag;
@ -69,6 +69,15 @@ public abstract class DefinedPacket
buf.writeBytes( b );
}
public static <T> T readStringMapKey(ByteBuf buf, Map<String, T> map)
{
String string = readString( buf );
T result = map.get( string );
Preconditions.checkArgument( result != null, "Unknown string key %s", string );
return result;
}
public static String readString(ByteBuf buf)
{
return readString( buf, Short.MAX_VALUE );
@ -110,12 +119,12 @@ public abstract class DefinedPacket
SpecificTag nbt = (SpecificTag) readTag( buf, protocolVersion );
JsonElement json = TagUtil.toJson( nbt );
return ComponentSerializer.deserialize( json );
return ChatSerializer.forVersion( protocolVersion ).deserialize( json );
} else
{
String string = readString( buf, maxStringLength );
return ComponentSerializer.deserialize( string );
return ChatSerializer.forVersion( protocolVersion ).deserialize( string );
}
}
@ -124,7 +133,7 @@ public abstract class DefinedPacket
SpecificTag nbt = (SpecificTag) readTag( buf, protocolVersion );
JsonElement json = TagUtil.toJson( nbt );
return ComponentSerializer.deserializeStyle( json );
return ChatSerializer.forVersion( protocolVersion ).deserializeStyle( json );
}
public static void writeEitherBaseComponent(Either<String, BaseComponent> message, ByteBuf buf, int protocolVersion)
@ -142,13 +151,13 @@ public abstract class DefinedPacket
{
if ( protocolVersion >= ProtocolConstants.MINECRAFT_1_20_3 )
{
JsonElement json = ComponentSerializer.toJson( message );
JsonElement json = ChatSerializer.forVersion( protocolVersion ).toJson( message );
SpecificTag nbt = TagUtil.fromJson( json );
writeTag( nbt, buf, protocolVersion );
} else
{
String string = ComponentSerializer.toString( message );
String string = ChatSerializer.forVersion( protocolVersion ).toString( message );
writeString( string, buf );
}
@ -156,7 +165,7 @@ public abstract class DefinedPacket
public static void writeComponentStyle(ComponentStyle style, ByteBuf buf, int protocolVersion)
{
JsonElement json = ComponentSerializer.toJson( style );
JsonElement json = ChatSerializer.forVersion( protocolVersion ).toJson( style );
SpecificTag nbt = TagUtil.fromJson( json );
writeTag( nbt, buf, protocolVersion );
@ -282,6 +291,31 @@ public abstract class DefinedPacket
}
}
public static void setVarInt(int value, ByteBuf output, int pos, int len)
{
switch ( len )
{
case 1:
output.setByte( pos, value );
break;
case 2:
output.setShort( pos, ( value & 0x7F | 0x80 ) << 8 | ( value >>> 7 & 0x7F ) );
break;
case 3:
output.setMedium( pos, ( value & 0x7F | 0x80 ) << 16 | ( value >>> 7 & 0x7F | 0x80 ) << 8 | ( value >>> 14 & 0x7F ) );
break;
case 4:
output.setInt( pos, ( value & 0x7F | 0x80 ) << 24 | ( value >>> 7 & 0x7F | 0x80 ) << 16 | ( value >>> 14 & 0x7F | 0x80 ) << 8 | ( value >>> 21 & 0x7F ) );
break;
case 5:
output.setInt( pos, ( value & 0x7F | 0x80 ) << 24 | ( value >>> 7 & 0x7F | 0x80 ) << 16 | ( value >>> 14 & 0x7F | 0x80 ) << 8 | ( value >>> 21 & 0x7F | 0x80 ) );
output.setByte( pos + 4, value >>> 28 );
break;
default:
throw new IllegalArgumentException( "Invalid varint len: " + len );
}
}
public static int readVarShort(ByteBuf buf)
{
int low = buf.readUnsignedShort();

View File

@ -99,7 +99,8 @@ public enum Protocol
map( ProtocolConstants.MINECRAFT_1_19_4, 0x23 ),
map( ProtocolConstants.MINECRAFT_1_20_2, 0x24 ),
map( ProtocolConstants.MINECRAFT_1_20_5, 0x26 ),
map( ProtocolConstants.MINECRAFT_1_21_2, 0x27 )
map( ProtocolConstants.MINECRAFT_1_21_2, 0x27 ),
map( ProtocolConstants.MINECRAFT_1_21_5, 0x26 )
);
TO_CLIENT.registerPacket(
Login.class,
@ -117,7 +118,8 @@ public enum Protocol
map( ProtocolConstants.MINECRAFT_1_19_4, 0x28 ),
map( ProtocolConstants.MINECRAFT_1_20_2, 0x29 ),
map( ProtocolConstants.MINECRAFT_1_20_5, 0x2B ),
map( ProtocolConstants.MINECRAFT_1_21_2, 0x2C )
map( ProtocolConstants.MINECRAFT_1_21_2, 0x2C ),
map( ProtocolConstants.MINECRAFT_1_21_5, 0x2B )
);
TO_CLIENT.registerPacket( Chat.class,
Chat::new,
@ -149,7 +151,8 @@ public enum Protocol
map( ProtocolConstants.MINECRAFT_1_20_2, 0x43 ),
map( ProtocolConstants.MINECRAFT_1_20_3, 0x45 ),
map( ProtocolConstants.MINECRAFT_1_20_5, 0x47 ),
map( ProtocolConstants.MINECRAFT_1_21_2, 0x4C )
map( ProtocolConstants.MINECRAFT_1_21_2, 0x4C ),
map( ProtocolConstants.MINECRAFT_1_21_5, 0x4B )
);
TO_CLIENT.registerPacket(
BossBar.class,
@ -160,7 +163,8 @@ public enum Protocol
map( ProtocolConstants.MINECRAFT_1_17, 0x0D ),
map( ProtocolConstants.MINECRAFT_1_19, 0x0A ),
map( ProtocolConstants.MINECRAFT_1_19_4, 0x0B ),
map( ProtocolConstants.MINECRAFT_1_20_2, 0x0A )
map( ProtocolConstants.MINECRAFT_1_20_2, 0x0A ),
map( ProtocolConstants.MINECRAFT_1_21_5, 0x09 )
);
TO_CLIENT.registerPacket(
PlayerListItem.class, // PlayerInfo
@ -191,7 +195,8 @@ public enum Protocol
map( ProtocolConstants.MINECRAFT_1_19, 0x0E ),
map( ProtocolConstants.MINECRAFT_1_19_3, 0x0D ),
map( ProtocolConstants.MINECRAFT_1_19_4, 0x0F ),
map( ProtocolConstants.MINECRAFT_1_20_2, 0x10 )
map( ProtocolConstants.MINECRAFT_1_20_2, 0x10 ),
map( ProtocolConstants.MINECRAFT_1_21_5, 0x0F )
);
TO_CLIENT.registerPacket(
ScoreboardObjective.class,
@ -210,7 +215,8 @@ public enum Protocol
map( ProtocolConstants.MINECRAFT_1_20_2, 0x5A ),
map( ProtocolConstants.MINECRAFT_1_20_3, 0x5C ),
map( ProtocolConstants.MINECRAFT_1_20_5, 0x5E ),
map( ProtocolConstants.MINECRAFT_1_21_2, 0x64 )
map( ProtocolConstants.MINECRAFT_1_21_2, 0x64 ),
map( ProtocolConstants.MINECRAFT_1_21_5, 0x63 )
);
TO_CLIENT.registerPacket(
ScoreboardScore.class,
@ -229,14 +235,16 @@ public enum Protocol
map( ProtocolConstants.MINECRAFT_1_20_2, 0x5D ),
map( ProtocolConstants.MINECRAFT_1_20_3, 0x5F ),
map( ProtocolConstants.MINECRAFT_1_20_5, 0x61 ),
map( ProtocolConstants.MINECRAFT_1_21_2, 0x68 )
map( ProtocolConstants.MINECRAFT_1_21_2, 0x68 ),
map( ProtocolConstants.MINECRAFT_1_21_5, 0x67 )
);
TO_CLIENT.registerPacket(
ScoreboardScoreReset.class,
ScoreboardScoreReset::new,
map( ProtocolConstants.MINECRAFT_1_20_3, 0x42 ),
map( ProtocolConstants.MINECRAFT_1_20_5, 0x44 ),
map( ProtocolConstants.MINECRAFT_1_21_2, 0x49 )
map( ProtocolConstants.MINECRAFT_1_21_2, 0x49 ),
map( ProtocolConstants.MINECRAFT_1_21_5, 0x48 )
);
TO_CLIENT.registerPacket(
ScoreboardDisplay.class,
@ -255,7 +263,8 @@ public enum Protocol
map( ProtocolConstants.MINECRAFT_1_20_2, 0x53 ),
map( ProtocolConstants.MINECRAFT_1_20_3, 0x55 ),
map( ProtocolConstants.MINECRAFT_1_20_5, 0x57 ),
map( ProtocolConstants.MINECRAFT_1_21_2, 0x5C )
map( ProtocolConstants.MINECRAFT_1_21_2, 0x5C ),
map( ProtocolConstants.MINECRAFT_1_21_5, 0x5B )
);
TO_CLIENT.registerPacket(
Team.class,
@ -274,7 +283,8 @@ public enum Protocol
map( ProtocolConstants.MINECRAFT_1_20_2, 0x5C ),
map( ProtocolConstants.MINECRAFT_1_20_3, 0x5E ),
map( ProtocolConstants.MINECRAFT_1_20_5, 0x60 ),
map( ProtocolConstants.MINECRAFT_1_21_2, 0x67 )
map( ProtocolConstants.MINECRAFT_1_21_2, 0x67 ),
map( ProtocolConstants.MINECRAFT_1_21_5, 0x66 )
);
TO_CLIENT.registerPacket(
PluginMessage.class,
@ -292,7 +302,8 @@ public enum Protocol
map( ProtocolConstants.MINECRAFT_1_19_3, 0x15 ),
map( ProtocolConstants.MINECRAFT_1_19_4, 0x17 ),
map( ProtocolConstants.MINECRAFT_1_20_2, 0x18 ),
map( ProtocolConstants.MINECRAFT_1_20_5, 0x19 )
map( ProtocolConstants.MINECRAFT_1_20_5, 0x19 ),
map( ProtocolConstants.MINECRAFT_1_21_5, 0x18 )
);
TO_CLIENT.registerPacket(
Kick.class,
@ -310,7 +321,8 @@ public enum Protocol
map( ProtocolConstants.MINECRAFT_1_19_3, 0x17 ),
map( ProtocolConstants.MINECRAFT_1_19_4, 0x1A ),
map( ProtocolConstants.MINECRAFT_1_20_2, 0x1B ),
map( ProtocolConstants.MINECRAFT_1_20_5, 0x1D )
map( ProtocolConstants.MINECRAFT_1_20_5, 0x1D ),
map( ProtocolConstants.MINECRAFT_1_21_5, 0x1C )
);
TO_CLIENT.registerPacket(
Title.class,
@ -330,7 +342,8 @@ public enum Protocol
map( ProtocolConstants.MINECRAFT_1_20_2, 0x61 ),
map( ProtocolConstants.MINECRAFT_1_20_3, 0x63 ),
map( ProtocolConstants.MINECRAFT_1_20_5, 0x65 ),
map( ProtocolConstants.MINECRAFT_1_21_2, 0x6C )
map( ProtocolConstants.MINECRAFT_1_21_2, 0x6C ),
map( ProtocolConstants.MINECRAFT_1_21_5, 0x6B )
);
TO_CLIENT.registerPacket(
ClearTitles.class,
@ -339,7 +352,8 @@ public enum Protocol
map( ProtocolConstants.MINECRAFT_1_19, 0x0D ),
map( ProtocolConstants.MINECRAFT_1_19_3, 0x0C ),
map( ProtocolConstants.MINECRAFT_1_19_4, 0x0E ),
map( ProtocolConstants.MINECRAFT_1_20_2, 0x0F )
map( ProtocolConstants.MINECRAFT_1_20_2, 0x0F ),
map( ProtocolConstants.MINECRAFT_1_21_5, 0x0E )
);
TO_CLIENT.registerPacket(
Subtitle.class,
@ -352,7 +366,8 @@ public enum Protocol
map( ProtocolConstants.MINECRAFT_1_20_2, 0x5F ),
map( ProtocolConstants.MINECRAFT_1_20_3, 0x61 ),
map( ProtocolConstants.MINECRAFT_1_20_5, 0x63 ),
map( ProtocolConstants.MINECRAFT_1_21_2, 0x6A )
map( ProtocolConstants.MINECRAFT_1_21_2, 0x6A ),
map( ProtocolConstants.MINECRAFT_1_21_5, 0x69 )
);
TO_CLIENT.registerPacket(
TitleTimes.class,
@ -365,7 +380,8 @@ public enum Protocol
map( ProtocolConstants.MINECRAFT_1_20_2, 0x62 ),
map( ProtocolConstants.MINECRAFT_1_20_3, 0x64 ),
map( ProtocolConstants.MINECRAFT_1_20_5, 0x66 ),
map( ProtocolConstants.MINECRAFT_1_21_2, 0x6D )
map( ProtocolConstants.MINECRAFT_1_21_2, 0x6D ),
map( ProtocolConstants.MINECRAFT_1_21_5, 0x6C )
);
TO_CLIENT.registerPacket(
SystemChat.class,
@ -377,7 +393,8 @@ public enum Protocol
map( ProtocolConstants.MINECRAFT_1_20_2, 0x67 ),
map( ProtocolConstants.MINECRAFT_1_20_3, 0x69 ),
map( ProtocolConstants.MINECRAFT_1_20_5, 0x6C ),
map( ProtocolConstants.MINECRAFT_1_21_2, 0x73 )
map( ProtocolConstants.MINECRAFT_1_21_2, 0x73 ),
map( ProtocolConstants.MINECRAFT_1_21_5, 0x72 )
);
TO_CLIENT.registerPacket(
PlayerListHeaderFooter.class,
@ -400,7 +417,8 @@ public enum Protocol
map( ProtocolConstants.MINECRAFT_1_20_2, 0x68 ),
map( ProtocolConstants.MINECRAFT_1_20_3, 0x6A ),
map( ProtocolConstants.MINECRAFT_1_20_5, 0x6D ),
map( ProtocolConstants.MINECRAFT_1_21_2, 0x74 )
map( ProtocolConstants.MINECRAFT_1_21_2, 0x74 ),
map( ProtocolConstants.MINECRAFT_1_21_5, 0x73 )
);
TO_CLIENT.registerPacket(
EntityStatus.class,
@ -418,7 +436,8 @@ public enum Protocol
map( ProtocolConstants.MINECRAFT_1_19_3, 0x19 ),
map( ProtocolConstants.MINECRAFT_1_19_4, 0x1C ),
map( ProtocolConstants.MINECRAFT_1_20_2, 0x1D ),
map( ProtocolConstants.MINECRAFT_1_20_5, 0x1F )
map( ProtocolConstants.MINECRAFT_1_20_5, 0x1F ),
map( ProtocolConstants.MINECRAFT_1_21_5, 0x1E )
);
TO_CLIENT.registerPacket(
Commands.class,
@ -431,7 +450,8 @@ public enum Protocol
map( ProtocolConstants.MINECRAFT_1_19, 0x0F ),
map( ProtocolConstants.MINECRAFT_1_19_3, 0x0E ),
map( ProtocolConstants.MINECRAFT_1_19_4, 0x10 ),
map( ProtocolConstants.MINECRAFT_1_20_2, 0x11 )
map( ProtocolConstants.MINECRAFT_1_20_2, 0x11 ),
map( ProtocolConstants.MINECRAFT_1_21_5, 0x10 )
);
TO_CLIENT.registerPacket(
GameState.class,
@ -446,7 +466,8 @@ public enum Protocol
map( ProtocolConstants.MINECRAFT_1_19_4, 0x1F ),
map( ProtocolConstants.MINECRAFT_1_20_2, 0x20 ),
map( ProtocolConstants.MINECRAFT_1_20_5, 0x22 ),
map( ProtocolConstants.MINECRAFT_1_21_2, 0x23 )
map( ProtocolConstants.MINECRAFT_1_21_2, 0x23 ),
map( ProtocolConstants.MINECRAFT_1_21_5, 0x22 )
);
TO_CLIENT.registerPacket(
ViewDistance.class,
@ -462,7 +483,8 @@ public enum Protocol
map( ProtocolConstants.MINECRAFT_1_20_2, 0x51 ),
map( ProtocolConstants.MINECRAFT_1_20_3, 0x53 ),
map( ProtocolConstants.MINECRAFT_1_20_5, 0x55 ),
map( ProtocolConstants.MINECRAFT_1_21_2, 0x59 )
map( ProtocolConstants.MINECRAFT_1_21_2, 0x59 ),
map( ProtocolConstants.MINECRAFT_1_21_5, 0x58 )
);
TO_CLIENT.registerPacket(
ServerData.class,
@ -474,7 +496,8 @@ public enum Protocol
map( ProtocolConstants.MINECRAFT_1_20_2, 0x47 ),
map( ProtocolConstants.MINECRAFT_1_20_3, 0x49 ),
map( ProtocolConstants.MINECRAFT_1_20_5, 0x4B ),
map( ProtocolConstants.MINECRAFT_1_21_2, 0x50 )
map( ProtocolConstants.MINECRAFT_1_21_2, 0x50 ),
map( ProtocolConstants.MINECRAFT_1_21_5, 0x4F )
);
TO_CLIENT.registerPacket(
PlayerListItemRemove.class,
@ -483,7 +506,8 @@ public enum Protocol
map( ProtocolConstants.MINECRAFT_1_19_4, 0x39 ),
map( ProtocolConstants.MINECRAFT_1_20_2, 0x3B ),
map( ProtocolConstants.MINECRAFT_1_20_5, 0x3D ),
map( ProtocolConstants.MINECRAFT_1_21_2, 0x3F )
map( ProtocolConstants.MINECRAFT_1_21_2, 0x3F ),
map( ProtocolConstants.MINECRAFT_1_21_5, 0x3E )
);
TO_CLIENT.registerPacket(
PlayerListItemUpdate.class,
@ -492,7 +516,8 @@ public enum Protocol
map( ProtocolConstants.MINECRAFT_1_19_4, 0x3A ),
map( ProtocolConstants.MINECRAFT_1_20_2, 0x3C ),
map( ProtocolConstants.MINECRAFT_1_20_5, 0x3E ),
map( ProtocolConstants.MINECRAFT_1_21_2, 0x40 )
map( ProtocolConstants.MINECRAFT_1_21_2, 0x40 ),
map( ProtocolConstants.MINECRAFT_1_21_5, 0x3F )
);
TO_CLIENT.registerPacket(
StartConfiguration.class,
@ -500,18 +525,21 @@ public enum Protocol
map( ProtocolConstants.MINECRAFT_1_20_2, 0x65 ),
map( ProtocolConstants.MINECRAFT_1_20_3, 0x67 ),
map( ProtocolConstants.MINECRAFT_1_20_5, 0x69 ),
map( ProtocolConstants.MINECRAFT_1_21_2, 0x70 )
map( ProtocolConstants.MINECRAFT_1_21_2, 0x70 ),
map( ProtocolConstants.MINECRAFT_1_21_5, 0x6F )
);
TO_CLIENT.registerPacket(
CookieRequest.class,
CookieRequest::new,
map( ProtocolConstants.MINECRAFT_1_20_5, 0x16 )
map( ProtocolConstants.MINECRAFT_1_20_5, 0x16 ),
map( ProtocolConstants.MINECRAFT_1_21_5, 0x15 )
);
TO_CLIENT.registerPacket(
StoreCookie.class,
StoreCookie::new,
map( ProtocolConstants.MINECRAFT_1_20_5, 0x6B ),
map( ProtocolConstants.MINECRAFT_1_21_2, 0x72 )
map( ProtocolConstants.MINECRAFT_1_21_2, 0x72 ),
map( ProtocolConstants.MINECRAFT_1_21_5, 0x71 )
);
TO_CLIENT.registerPacket(
Transfer.class,
@ -965,6 +993,8 @@ public enum Protocol
}
ProtocolData data = protocols.get( protocol );
Preconditions.checkState( data.packetConstructors[mapping.packetID] == null, "Duplicate packet mapping (%s)", mapping.packetID );
data.packetMap.put( packetClass, mapping.packetID );
data.packetConstructors[mapping.packetID] = constructor;
}

View File

@ -49,6 +49,7 @@ public class ProtocolConstants
public static final int MINECRAFT_1_21 = 767;
public static final int MINECRAFT_1_21_2 = 768;
public static final int MINECRAFT_1_21_4 = 769;
public static final int MINECRAFT_1_21_5 = 770;
public static final List<String> SUPPORTED_VERSIONS;
public static final List<Integer> SUPPORTED_VERSION_IDS;
@ -112,13 +113,14 @@ public class ProtocolConstants
ProtocolConstants.MINECRAFT_1_20_5,
ProtocolConstants.MINECRAFT_1_21,
ProtocolConstants.MINECRAFT_1_21_2,
ProtocolConstants.MINECRAFT_1_21_4
ProtocolConstants.MINECRAFT_1_21_4,
ProtocolConstants.MINECRAFT_1_21_5
);
if ( SNAPSHOT_SUPPORT )
{
// supportedVersions.add( "1.21.x" );
// supportedVersionIds.add( ProtocolConstants.MINECRAFT_1_21_4 );
// supportedVersionIds.add( ProtocolConstants.MINECRAFT_1_21_5 );
}
SUPPORTED_VERSIONS = supportedVersions.build();

View File

@ -82,13 +82,27 @@ public final class TagUtil
{
List<JsonElement> jsonArray = ( (JsonArray) json ).asList();
if ( jsonArray.isEmpty() )
Integer listType = null;
for ( JsonElement jsonEl : jsonArray )
{
int type = fromJson( jsonEl ).tagType();
if ( listType == null )
{
listType = type;
} else if ( listType != type )
{
listType = Tag.TAG_COMPOUND;
break;
}
}
if ( listType == null )
{
return new ListTag( Tag.TAG_END, Collections.emptyList() );
}
SpecificTag listTag;
int listType = fromJson( jsonArray.get( 0 ) ).tagType();
switch ( listType )
{
case Tag.TAG_BYTE:
@ -124,7 +138,7 @@ public final class TagUtil
for ( JsonElement jsonEl : jsonArray )
{
SpecificTag subTag = fromJson( jsonEl );
if ( !( subTag instanceof CompoundTag ) )
if ( listType == Tag.TAG_COMPOUND && !( subTag instanceof CompoundTag ) )
{
CompoundTag wrapper = new CompoundTag();
wrapper.add( "", subTag );

View File

@ -1,26 +0,0 @@
package net.md_5.bungee.protocol;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToMessageEncoder;
import java.util.List;
/**
* Prepend length of the message as a Varint21 using an extra buffer for the
* length, avoiding copying packet data
*/
@ChannelHandler.Sharable
public class Varint21LengthFieldExtraBufPrepender extends MessageToMessageEncoder<ByteBuf>
{
@Override
protected void encode(ChannelHandlerContext ctx, ByteBuf msg, List<Object> out) throws Exception
{
int bodyLen = msg.readableBytes();
ByteBuf lenBuf = ctx.alloc().ioBuffer( Varint21LengthFieldPrepender.varintSize( bodyLen ) );
DefinedPacket.writeVarInt( bodyLen, lenBuf );
out.add( lenBuf );
out.add( msg.retain() );
}
}

View File

@ -1,58 +0,0 @@
package net.md_5.bungee.protocol;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToMessageEncoder;
import java.util.List;
import lombok.Setter;
/**
* Prepend length of the message as a Varint21 by writing length and data to a
* new buffer
*/
public class Varint21LengthFieldPrepender extends MessageToMessageEncoder<ByteBuf>
{
@Setter
private boolean compose = true;
@Override
protected void encode(ChannelHandlerContext ctx, ByteBuf msg, List<Object> list) throws Exception
{
int bodyLen = msg.readableBytes();
int headerLen = varintSize( bodyLen );
if ( compose )
{
ByteBuf buf = ctx.alloc().directBuffer( headerLen );
DefinedPacket.writeVarInt( bodyLen, buf );
list.add( ctx.alloc().compositeDirectBuffer( 2 ).addComponents( true, buf, msg.retain() ) );
} else
{
ByteBuf buf = ctx.alloc().directBuffer( headerLen + bodyLen );
DefinedPacket.writeVarInt( bodyLen, buf );
buf.writeBytes( msg );
list.add( buf );
}
}
static int varintSize(int paramInt)
{
if ( ( paramInt & 0xFFFFFF80 ) == 0 )
{
return 1;
}
if ( ( paramInt & 0xFFFFC000 ) == 0 )
{
return 2;
}
if ( ( paramInt & 0xFFE00000 ) == 0 )
{
return 3;
}
if ( ( paramInt & 0xF0000000 ) == 0 )
{
return 4;
}
return 5;
}
}

View File

@ -0,0 +1,54 @@
package net.md_5.bungee.protocol.channel;
import io.netty.channel.Channel;
import io.netty.channel.ChannelInitializer;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.NoArgsConstructor;
/**
* This class hold a netty channel initializer that calls the given {@link ChannelAcceptor}.
* Use {@link BungeeChannelInitializer#create(ChannelAcceptor)} to create a new instance.
* <p>
* Please note that this API is unsafe and doesn't provide any guarantees about
* the stability of the channel pipeline or the API itself. Use at your own
* risk.
*/
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public abstract class BungeeChannelInitializer
{
public abstract ChannelAcceptor getChannelAcceptor();
public abstract ChannelInitializer<Channel> getChannelInitializer();
/**
* Creates a new instance of BungeeChannelInitializer
*
* @param acceptor the {@link ChannelAcceptor} that will accept the channel
* and initializer the pipeline
* @return {@link BungeeChannelInitializer} containing a cached
* {@link ChannelInitializer} that will call the acceptor
*/
public static BungeeChannelInitializer create(ChannelAcceptor acceptor)
{
return new BungeeChannelInitializer()
{
@Getter
private final ChannelAcceptor channelAcceptor = acceptor;
@Getter // cache the ChannelInitializer
private final ChannelInitializer<Channel> channelInitializer = new ChannelInitializer<Channel>()
{
@Override
protected void initChannel(Channel channel) throws Exception
{
if ( !getChannelAcceptor().accept( channel ) )
{
channel.close();
}
}
};
};
}
}

View File

@ -0,0 +1,16 @@
package net.md_5.bungee.protocol.channel;
import io.netty.channel.Channel;
@FunctionalInterface
public interface ChannelAcceptor
{
/**
* Inside this method the pipeline should be initialized.
*
* @param channel the channel to be accepted and initialized
* @return if the channel was accepted
*/
boolean accept(Channel channel);
}

View File

@ -25,6 +25,7 @@ public class ClientChat extends DefinedPacket
private boolean signedPreview;
private ChatChain chain;
private SeenMessages seenMessages;
private byte checksum;
@Override
public void read(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion)
@ -57,6 +58,11 @@ public class ClientChat extends DefinedPacket
chain = new ChatChain();
chain.read( buf, direction, protocolVersion );
}
if ( protocolVersion >= ProtocolConstants.MINECRAFT_1_21_5 )
{
checksum = buf.readByte();
}
}
@Override
@ -87,6 +93,11 @@ public class ClientChat extends DefinedPacket
{
chain.write( buf, direction, protocolVersion );
}
if ( protocolVersion >= ProtocolConstants.MINECRAFT_1_21_5 )
{
buf.writeByte( checksum );
}
}
@Override

View File

@ -28,6 +28,7 @@ public class ClientCommand extends DefinedPacket
private boolean signedPreview;
private ChatChain chain;
private SeenMessages seenMessages;
private byte checksum;
@Override
public void read(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion)
@ -68,6 +69,11 @@ public class ClientCommand extends DefinedPacket
chain = new ChatChain();
chain.read( buf, direction, protocolVersion );
}
if ( protocolVersion >= ProtocolConstants.MINECRAFT_1_21_5 )
{
checksum = buf.readByte();
}
}
@Override
@ -101,6 +107,11 @@ public class ClientCommand extends DefinedPacket
{
chain.write( buf, direction, protocolVersion );
}
if ( protocolVersion >= ProtocolConstants.MINECRAFT_1_21_5 )
{
buf.writeByte( checksum );
}
}
@Override

View File

@ -4,6 +4,7 @@ import com.google.common.base.Preconditions;
import com.mojang.brigadier.Command;
import com.mojang.brigadier.StringReader;
import com.mojang.brigadier.arguments.ArgumentType;
import com.mojang.brigadier.arguments.BoolArgumentType;
import com.mojang.brigadier.arguments.DoubleArgumentType;
import com.mojang.brigadier.arguments.FloatArgumentType;
import com.mojang.brigadier.arguments.IntegerArgumentType;
@ -303,7 +304,7 @@ public class Commands extends DefinedPacket
}
@Data
private static class ArgumentRegistry
public static class ArgumentRegistry
{
private static final Map<String, ArgumentSerializer> PROVIDERS = new HashMap<>();
@ -312,6 +313,7 @@ public class Commands extends DefinedPacket
private static final ArgumentSerializer[] IDS_1_19_4;
private static final ArgumentSerializer[] IDS_1_20_3;
private static final ArgumentSerializer[] IDS_1_20_5;
private static final ArgumentSerializer[] IDS_1_21_5;
private static final Map<Class<?>, ProperArgumentSerializer<?>> PROPER_PROVIDERS = new HashMap<>();
//
private static final ArgumentSerializer<Void> VOID = new ArgumentSerializer<Void>()
@ -327,18 +329,29 @@ public class Commands extends DefinedPacket
{
}
};
private static final ArgumentSerializer<Boolean> BOOLEAN = new ArgumentSerializer<Boolean>()
private static final ProperArgumentSerializer<BoolArgumentType> BOOLEAN = new ProperArgumentSerializer<BoolArgumentType>()
{
@Override
protected Boolean read(ByteBuf buf)
protected BoolArgumentType read(ByteBuf buf)
{
return buf.readBoolean();
return BoolArgumentType.bool();
}
@Override
protected void write(ByteBuf buf, Boolean t)
protected void write(ByteBuf buf, BoolArgumentType t)
{
buf.writeBoolean( t );
}
@Override
protected int getIntKey()
{
return 0;
}
@Override
protected String getKey()
{
return "brigadier:bool";
}
};
private static final ArgumentSerializer<Byte> BYTE = new ArgumentSerializer<Byte>()
@ -355,7 +368,7 @@ public class Commands extends DefinedPacket
buf.writeByte( t );
}
};
private static final ArgumentSerializer<FloatArgumentType> FLOAT_RANGE = new ArgumentSerializer<FloatArgumentType>()
private static final ProperArgumentSerializer<FloatArgumentType> FLOAT_RANGE = new ProperArgumentSerializer<FloatArgumentType>()
{
@Override
protected FloatArgumentType read(ByteBuf buf)
@ -383,8 +396,20 @@ public class Commands extends DefinedPacket
buf.writeFloat( t.getMaximum() );
}
}
@Override
protected int getIntKey()
{
return 1;
}
@Override
protected String getKey()
{
return "brigadier:float";
}
};
private static final ArgumentSerializer<DoubleArgumentType> DOUBLE_RANGE = new ArgumentSerializer<DoubleArgumentType>()
private static final ProperArgumentSerializer<DoubleArgumentType> DOUBLE_RANGE = new ProperArgumentSerializer<DoubleArgumentType>()
{
@Override
protected DoubleArgumentType read(ByteBuf buf)
@ -412,8 +437,20 @@ public class Commands extends DefinedPacket
buf.writeDouble( t.getMaximum() );
}
}
@Override
protected int getIntKey()
{
return 2;
}
@Override
protected String getKey()
{
return "brigadier:double";
}
};
private static final ArgumentSerializer<IntegerArgumentType> INTEGER_RANGE = new ArgumentSerializer<IntegerArgumentType>()
private static final ProperArgumentSerializer<IntegerArgumentType> INTEGER_RANGE = new ProperArgumentSerializer<IntegerArgumentType>()
{
@Override
protected IntegerArgumentType read(ByteBuf buf)
@ -441,6 +478,18 @@ public class Commands extends DefinedPacket
buf.writeInt( t.getMaximum() );
}
}
@Override
protected int getIntKey()
{
return 3;
}
@Override
protected String getKey()
{
return "brigadier:integer";
}
};
private static final ArgumentSerializer<Integer> INTEGER = new ArgumentSerializer<Integer>()
{
@ -456,7 +505,7 @@ public class Commands extends DefinedPacket
buf.writeInt( t );
}
};
private static final ArgumentSerializer<LongArgumentType> LONG_RANGE = new ArgumentSerializer<LongArgumentType>()
private static final ProperArgumentSerializer<LongArgumentType> LONG_RANGE = new ProperArgumentSerializer<LongArgumentType>()
{
@Override
protected LongArgumentType read(ByteBuf buf)
@ -484,6 +533,18 @@ public class Commands extends DefinedPacket
buf.writeLong( t.getMaximum() );
}
}
@Override
protected int getIntKey()
{
return 4;
}
@Override
protected String getKey()
{
return "brigadier:long";
}
};
private static final ProperArgumentSerializer<StringArgumentType> STRING = new ProperArgumentSerializer<StringArgumentType>()
{
@ -539,11 +600,20 @@ public class Commands extends DefinedPacket
static
{
register( "brigadier:bool", VOID );
register( "brigadier:bool", BOOLEAN );
PROPER_PROVIDERS.put( BoolArgumentType.class, BOOLEAN );
register( "brigadier:float", FLOAT_RANGE );
PROPER_PROVIDERS.put( FloatArgumentType.class, FLOAT_RANGE );
register( "brigadier:double", DOUBLE_RANGE );
PROPER_PROVIDERS.put( DoubleArgumentType.class, DOUBLE_RANGE );
register( "brigadier:integer", INTEGER_RANGE );
register( "brigadier:long", LONG_RANGE );
PROPER_PROVIDERS.put( IntegerArgumentType.class, INTEGER_RANGE );
register( "brigadier:long", LONG_RANGE ); // 1.14+
PROPER_PROVIDERS.put( LongArgumentType.class, LONG_RANGE );
register( "brigadier:string", STRING );
PROPER_PROVIDERS.put( StringArgumentType.class, STRING );
@ -858,6 +928,65 @@ public class Commands extends DefinedPacket
get( "minecraft:loot_predicate", VOID ),
get( "minecraft:loot_modifier", VOID )
};
IDS_1_21_5 = new ArgumentSerializer[]
{
get( "brigadier:bool", VOID ),
get( "brigadier:float", FLOAT_RANGE ),
get( "brigadier:double", DOUBLE_RANGE ),
get( "brigadier:integer", INTEGER_RANGE ),
get( "brigadier:long", LONG_RANGE ),
get( "brigadier:string", STRING ),
get( "minecraft:entity", BYTE ),
get( "minecraft:game_profile", VOID ),
get( "minecraft:block_pos", VOID ),
get( "minecraft:column_pos", VOID ),
get( "minecraft:vec3", VOID ),
get( "minecraft:vec2", VOID ),
get( "minecraft:block_state", VOID ),
get( "minecraft:block_predicate", VOID ),
get( "minecraft:item_stack", VOID ),
get( "minecraft:item_predicate", VOID ),
get( "minecraft:color", VOID ),
get( "minecraft:component", VOID ),
get( "minecraft:style", VOID ),
get( "minecraft:message", VOID ),
get( "minecraft:nbt_compound_tag", VOID ),
get( "minecraft:nbt_tag", VOID ),
get( "minecraft:nbt_path", VOID ),
get( "minecraft:objective", VOID ),
get( "minecraft:objective_criteria", VOID ),
get( "minecraft:operation", VOID ),
get( "minecraft:particle", VOID ),
get( "minecraft:angle", VOID ),
get( "minecraft:rotation", VOID ),
get( "minecraft:scoreboard_slot", VOID ),
get( "minecraft:score_holder", BYTE ),
get( "minecraft:swizzle", VOID ),
get( "minecraft:team", VOID ),
get( "minecraft:item_slot", VOID ),
get( "minecraft:item_slots", VOID ),
get( "minecraft:resource_location", VOID ),
get( "minecraft:function", VOID ),
get( "minecraft:entity_anchor", VOID ),
get( "minecraft:int_range", VOID ),
get( "minecraft:float_range", VOID ),
get( "minecraft:dimension", VOID ),
get( "minecraft:gamemode", VOID ),
get( "minecraft:time", INTEGER ),
get( "minecraft:resource_or_tag", RAW_STRING ),
get( "minecraft:resource_or_tag_key", RAW_STRING ),
get( "minecraft:resource", RAW_STRING ),
get( "minecraft:resource_key", RAW_STRING ),
get( "minecraft:resource_selector", RAW_STRING ),
get( "minecraft:template_mirror", VOID ),
get( "minecraft:template_rotation", VOID ),
get( "minecraft:uuid", VOID ),
get( "minecraft:heightmap", VOID ),
get( "minecraft:loot_table", VOID ),
get( "minecraft:loot_predicate", VOID ),
get( "minecraft:loot_modifier", VOID )
};
}
private static void register(String name, ArgumentSerializer serializer)
@ -870,6 +999,404 @@ public class Commands extends DefinedPacket
return serializer;
}
/**
* Returns the Minecraft ArgumentType {@code minecraft:entity}.
* @param singleEntity if the argument restrict to only one entity
* @param onlyPlayers if the argument restrict to players only
* @return an ArgumentType instance
*/
public static ArgumentType<?> minecraftEntity(boolean singleEntity, boolean onlyPlayers)
{
byte flags = 0;
if ( singleEntity )
{
flags |= 1;
}
if ( onlyPlayers )
{
flags |= 2;
}
return minecraftArgumentType( "minecraft:entity", flags );
}
/**
* Returns the Minecraft ArgumentType {@code minecraft:game_profile}.
* @return an ArgumentType instance
*/
public static ArgumentType<?> minecraftGameProfile()
{
return minecraftArgumentType( "minecraft:game_profile", null );
}
/**
* Returns the Minecraft ArgumentType {@code minecraft:block_pos}.
* @return an ArgumentType instance
*/
public static ArgumentType<?> minecraftBlockPos()
{
return minecraftArgumentType( "minecraft:block_pos", null );
}
/**
* Returns the Minecraft ArgumentType {@code minecraft:column_pos}.
* @return an ArgumentType instance
*/
public static ArgumentType<?> minecraftColumnPos()
{
return minecraftArgumentType( "minecraft:column_pos", null );
}
/**
* Returns the Minecraft ArgumentType {@code minecraft:vec3}.
* @return an ArgumentType instance
*/
public static ArgumentType<?> minecraftVec3()
{
return minecraftArgumentType( "minecraft:vec3", null );
}
/**
* Returns the Minecraft ArgumentType {@code minecraft:vec2}.
* @return an ArgumentType instance
*/
public static ArgumentType<?> minecraftVec2()
{
return minecraftArgumentType( "minecraft:vec2", null );
}
/**
* Returns the Minecraft ArgumentType {@code minecraft:block_state}.
* @return an ArgumentType instance
*/
public static ArgumentType<?> minecraftBlockState()
{
return minecraftArgumentType( "minecraft:block_state", null );
}
/**
* Returns the Minecraft ArgumentType {@code minecraft:block_predicate}.
* @return an ArgumentType instance
*/
public static ArgumentType<?> minecraftBlockPredicate()
{
return minecraftArgumentType( "minecraft:block_predicate", null );
}
/**
* Returns the Minecraft ArgumentType {@code minecraft:item_stack}.
* @return an ArgumentType instance
*/
public static ArgumentType<?> minecraftItemStack()
{
return minecraftArgumentType( "minecraft:item_stack", null );
}
/**
* Returns the Minecraft ArgumentType {@code minecraft:item_predicate}.
* @return an ArgumentType instance
*/
public static ArgumentType<?> minecraftItemPredicate()
{
return minecraftArgumentType( "minecraft:item_predicate", null );
}
/**
* Returns the Minecraft ArgumentType {@code minecraft:color}.
* @return an ArgumentType instance
*/
public static ArgumentType<?> minecraftColor()
{
return minecraftArgumentType( "minecraft:color", null );
}
/**
* Returns the Minecraft ArgumentType {@code minecraft:component}.
* @return an ArgumentType instance
*/
public static ArgumentType<?> minecraftComponent()
{
return minecraftArgumentType( "minecraft:component", null );
}
/**
* Returns the Minecraft ArgumentType {@code minecraft:message}.
* @return an ArgumentType instance
*/
public static ArgumentType<?> minecraftMessage()
{
return minecraftArgumentType( "minecraft:message", null );
}
/**
* Returns the Minecraft ArgumentType {@code minecraft:nbt_compound_tag}.
* @return an ArgumentType instance
*/
public static ArgumentType<?> minecraftNBTCompoundTag()
{
return minecraftArgumentType( "minecraft:nbt_compound_tag", null );
}
/**
* Returns the Minecraft ArgumentType {@code minecraft:nbt_tag}.
* @return an ArgumentType instance
*/
public static ArgumentType<?> minecraftNBTTag()
{
return minecraftArgumentType( "minecraft:nbt_tag", null );
}
/**
* Returns the Minecraft ArgumentType {@code minecraft:nbt}.
* @return an ArgumentType instance
*/
public static ArgumentType<?> minecraftNBT()
{
return minecraftArgumentType( "minecraft:nbt", null );
}
/**
* Returns the Minecraft ArgumentType {@code minecraft:nbt_path}.
* @return an ArgumentType instance
*/
public static ArgumentType<?> minecraftNBTPath()
{
return minecraftArgumentType( "minecraft:nbt_path", null );
}
/**
* Returns the Minecraft ArgumentType {@code minecraft:objective}.
* @return an ArgumentType instance
*/
public static ArgumentType<?> minecraftObjective()
{
return minecraftArgumentType( "minecraft:objective", null );
}
/**
* Returns the Minecraft ArgumentType {@code minecraft:objective_criteria}.
* @return an ArgumentType instance
*/
public static ArgumentType<?> minecraftObjectiveCriteria()
{
return minecraftArgumentType( "minecraft:objective_criteria", null );
}
/**
* Returns the Minecraft ArgumentType {@code minecraft:operation}.
* @return an ArgumentType instance
*/
public static ArgumentType<?> minecraftOperation()
{
return minecraftArgumentType( "minecraft:operation", null );
}
/**
* Returns the Minecraft ArgumentType {@code minecraft:particle}.
* @return an ArgumentType instance
*/
public static ArgumentType<?> minecraftParticle()
{
return minecraftArgumentType( "minecraft:particle", null );
}
/**
* Returns the Minecraft ArgumentType {@code minecraft:rotation}.
* @return an ArgumentType instance
*/
public static ArgumentType<?> minecraftRotation()
{
return minecraftArgumentType( "minecraft:rotation", null );
}
/**
* Returns the Minecraft ArgumentType {@code minecraft:scoreboard_slot}.
* @return an ArgumentType instance
*/
public static ArgumentType<?> minecraftScoreboardSlot()
{
return minecraftArgumentType( "minecraft:scoreboard_slot", null );
}
/**
* Returns the Minecraft ArgumentType {@code minecraft:score_holder}.
* @param allowMultiple if the argument allows multiple entities
* @return an ArgumentType instance
*/
public static ArgumentType<?> minecraftScoreHolder(boolean allowMultiple)
{
byte flags = 0;
if ( allowMultiple )
{
flags |= 1;
}
return minecraftArgumentType( "minecraft:score_holder", flags );
}
/**
* Returns the Minecraft ArgumentType {@code minecraft:swizzle}.
* @return an ArgumentType instance
*/
public static ArgumentType<?> minecraftSwizzle()
{
return minecraftArgumentType( "minecraft:swizzle", null );
}
/**
* Returns the Minecraft ArgumentType {@code minecraft:team}.
* @return an ArgumentType instance
*/
public static ArgumentType<?> minecraftTeam()
{
return minecraftArgumentType( "minecraft:team", null );
}
/**
* Returns the Minecraft ArgumentType {@code minecraft:item_slot}.
* @return an ArgumentType instance
*/
public static ArgumentType<?> minecraftItemSlot()
{
return minecraftArgumentType( "minecraft:item_slot", null );
}
/**
* Returns the Minecraft ArgumentType {@code minecraft:resource_location}.
* @return an ArgumentType instance
*/
public static ArgumentType<?> minecraftResourceLocation()
{
return minecraftArgumentType( "minecraft:resource_location", null );
}
/**
* Returns the Minecraft ArgumentType {@code minecraft:mob_effect}.
* @return an ArgumentType instance
*/
public static ArgumentType<?> minecraftMobEffect()
{
return minecraftArgumentType( "minecraft:mob_effect", null );
}
/**
* Returns the Minecraft ArgumentType {@code minecraft:function}.
* @return an ArgumentType instance
*/
public static ArgumentType<?> minecraftFunction()
{
return minecraftArgumentType( "minecraft:function", null );
}
/**
* Returns the Minecraft ArgumentType {@code minecraft:entity_anchor}.
* @return an ArgumentType instance
*/
public static ArgumentType<?> minecraftEntityAnchor()
{
return minecraftArgumentType( "minecraft:entity_anchor", null );
}
/**
* Returns the Minecraft ArgumentType {@code minecraft:int_range}.
* @return an ArgumentType instance
*/
public static ArgumentType<?> minecraftIntRange()
{
return minecraftArgumentType( "minecraft:int_range", null );
}
/**
* Returns the Minecraft ArgumentType {@code minecraft:float_range}.
* @return an ArgumentType instance
*/
public static ArgumentType<?> minecraftFloatRange()
{
return minecraftArgumentType( "minecraft:float_range", null );
}
/**
* Returns the Minecraft ArgumentType {@code minecraft:item_enchantment}.
* @return an ArgumentType instance
*/
public static ArgumentType<?> minecraftItemEnchantment()
{
return minecraftArgumentType( "minecraft:item_enchantment", null );
}
/**
* Returns the Minecraft ArgumentType {@code minecraft:entity_summon}.
* @return an ArgumentType instance
*/
public static ArgumentType<?> minecraftEntitySummon()
{
return minecraftArgumentType( "minecraft:entity_summon", null );
}
/**
* Returns the Minecraft ArgumentType {@code minecraft:dimension}.
* @return an ArgumentType instance
*/
public static ArgumentType<?> minecraftDimension()
{
return minecraftArgumentType( "minecraft:dimension", null );
}
/**
* Returns the Minecraft ArgumentType {@code minecraft:time}.
* @return an ArgumentType instance
*/
public static ArgumentType<?> minecraftTime()
{
return minecraftArgumentType( "minecraft:time", null );
}
/**
* Returns the Minecraft ArgumentType {@code minecraft:uuid}.
* @return an ArgumentType instance
*/
public static ArgumentType<?> minecraftUUID()
{
return minecraftArgumentType( "minecraft:uuid", null );
}
/**
* Returns the Minecraft ArgumentType {@code minecraft:angle}.
* @return an ArgumentType instance
*/
public static ArgumentType<?> minecraftAngle()
{
return minecraftArgumentType( "minecraft:angle", null );
}
/**
* Returns the Minecraft ArgumentType {@code minecraft:resource}.
* @param rawString the raw string for the argument
* @return an ArgumentType instance
*/
public static ArgumentType<?> minecraftResource(String rawString)
{
return minecraftArgumentType( "minecraft:resource", rawString );
}
/**
* Returns the Minecraft ArgumentType {@code minecraft:resource_or_tag}.
* @param rawString the raw string for the argument
* @return an ArgumentType instance
*/
public static ArgumentType<?> minecraftResourceOrTag(String rawString)
{
return minecraftArgumentType( "minecraft:resource_or_tag", rawString );
}
private static ArgumentType<?> minecraftArgumentType(String key, Object rawValue)
{
ArgumentSerializer reader = PROVIDERS.get( key );
Preconditions.checkArgument( reader != null, "No provider for argument " + key );
return new DummyType( key, reader, rawValue );
}
private static ArgumentType<?> read(ByteBuf buf, int protocolVersion)
{
Object key;
@ -879,7 +1406,10 @@ public class Commands extends DefinedPacket
{
key = readVarInt( buf );
if ( protocolVersion >= ProtocolConstants.MINECRAFT_1_20_5 )
if ( protocolVersion >= ProtocolConstants.MINECRAFT_1_21_5 )
{
reader = IDS_1_21_5[(Integer) key];
} else if ( protocolVersion >= ProtocolConstants.MINECRAFT_1_20_5 )
{
reader = IDS_1_20_5[(Integer) key];
} else if ( protocolVersion >= ProtocolConstants.MINECRAFT_1_20_3 )
@ -999,11 +1529,15 @@ public class Commands extends DefinedPacket
private static String getKey(SuggestionProvider<DummyProvider> provider)
{
Preconditions.checkArgument( provider instanceof DummyProvider, "Non dummy provider " + provider );
Preconditions.checkNotNull( provider );
if ( provider instanceof DummyProvider )
{
return ( (DummyProvider) provider ).key;
}
return ( (DummyProvider) ASK_SERVER ).key;
}
@Data
private static final class DummyProvider implements SuggestionProvider<DummyProvider>
{

View File

@ -6,8 +6,8 @@ import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import net.md_5.bungee.api.chat.BaseComponent;
import net.md_5.bungee.chat.ComponentSerializer;
import net.md_5.bungee.protocol.AbstractPacketHandler;
import net.md_5.bungee.protocol.ChatSerializer;
import net.md_5.bungee.protocol.DefinedPacket;
import net.md_5.bungee.protocol.Protocol;
import net.md_5.bungee.protocol.ProtocolConstants;
@ -26,7 +26,7 @@ public class Kick extends DefinedPacket
{
if ( protocol == Protocol.LOGIN )
{
message = ComponentSerializer.deserialize( readString( buf ) );
message = ChatSerializer.forVersion( protocolVersion ).deserialize( readString( buf ) );
} else
{
message = readBaseComponent( buf, protocolVersion );
@ -38,7 +38,7 @@ public class Kick extends DefinedPacket
{
if ( protocol == Protocol.LOGIN )
{
writeString( ComponentSerializer.toString( message ), buf );
writeString( ChatSerializer.forVersion( protocolVersion ).toString( message ), buf );
} else
{
writeBaseComponent( message, buf, protocolVersion );

View File

@ -22,19 +22,21 @@ import net.md_5.bungee.protocol.ProtocolConstants;
public class PluginMessage extends DefinedPacket
{
public static final String BUNGEE_CHANNEL_LEGACY = "BungeeCord";
public static final String BUNGEE_CHANNEL_MODERN = "bungeecord:main";
public static final Function<String, String> MODERNISE = new Function<String, String>()
{
@Override
public String apply(String tag)
{
// Transform as per Bukkit
if ( tag.equals( "BungeeCord" ) )
if ( tag.equals( PluginMessage.BUNGEE_CHANNEL_LEGACY ) )
{
return "bungeecord:main";
return PluginMessage.BUNGEE_CHANNEL_MODERN;
}
if ( tag.equals( "bungeecord:main" ) )
if ( tag.equals( PluginMessage.BUNGEE_CHANNEL_MODERN ) )
{
return "BungeeCord";
return PluginMessage.BUNGEE_CHANNEL_LEGACY;
}
// Code that gets to here is UNLIKELY to be viable on the Bukkit side of side things,

View File

@ -1,10 +1,15 @@
package net.md_5.bungee.protocol.packet;
import com.google.common.collect.ImmutableMap;
import io.netty.buffer.ByteBuf;
import java.util.Arrays;
import java.util.Map;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.RequiredArgsConstructor;
import net.md_5.bungee.api.chat.BaseComponent;
import net.md_5.bungee.protocol.AbstractPacketHandler;
import net.md_5.bungee.protocol.DefinedPacket;
@ -26,8 +31,8 @@ public class Team extends DefinedPacket
private Either<String, BaseComponent> displayName;
private Either<String, BaseComponent> prefix;
private Either<String, BaseComponent> suffix;
private String nameTagVisibility;
private String collisionRule;
private NameTagVisibility nameTagVisibility;
private CollisionRule collisionRule;
private int color;
private byte friendlyFire;
private String[] players;
@ -60,10 +65,18 @@ public class Team extends DefinedPacket
displayName = readEitherBaseComponent( buf, protocolVersion, false );
}
friendlyFire = buf.readByte();
nameTagVisibility = readString( buf );
if ( protocolVersion >= ProtocolConstants.MINECRAFT_1_21_5 )
{
nameTagVisibility = NameTagVisibility.BY_ID[readVarInt( buf )];
collisionRule = CollisionRule.BY_ID[readVarInt( buf )];
} else
{
nameTagVisibility = readStringMapKey( buf, NameTagVisibility.BY_NAME );
if ( protocolVersion >= ProtocolConstants.MINECRAFT_1_9 )
{
collisionRule = readString( buf );
collisionRule = readStringMapKey( buf, CollisionRule.BY_NAME );
}
}
color = ( protocolVersion >= ProtocolConstants.MINECRAFT_1_13 ) ? readVarInt( buf ) : buf.readByte();
if ( protocolVersion >= ProtocolConstants.MINECRAFT_1_13 )
@ -97,10 +110,17 @@ public class Team extends DefinedPacket
writeEitherBaseComponent( suffix, buf, protocolVersion );
}
buf.writeByte( friendlyFire );
writeString( nameTagVisibility, buf );
if ( protocolVersion >= ProtocolConstants.MINECRAFT_1_21_5 )
{
writeVarInt( nameTagVisibility.ordinal(), buf );
writeVarInt( collisionRule.ordinal(), buf );
} else
{
writeString( nameTagVisibility.getKey(), buf );
if ( protocolVersion >= ProtocolConstants.MINECRAFT_1_9 )
{
writeString( collisionRule, buf );
writeString( collisionRule.getKey(), buf );
}
}
if ( protocolVersion >= ProtocolConstants.MINECRAFT_1_13 )
@ -128,4 +148,66 @@ public class Team extends DefinedPacket
{
handler.handle( this );
}
@Getter
@RequiredArgsConstructor
public enum NameTagVisibility
{
ALWAYS( "always" ),
NEVER( "never" ),
HIDE_FOR_OTHER_TEAMS( "hideForOtherTeams" ),
HIDE_FOR_OWN_TEAM( "hideForOwnTeam" ),
// 1.9 (and possibly other versions) appear to treat unknown values differently (always render rather than subject to spectator mode, friendly invisibles, etc).
// we allow the empty value to achieve this in case it is potentially useful even though this is unsupported and its usage may be a bug (#3780).
UNKNOWN( "" );
//
private final String key;
//
private static final Map<String, NameTagVisibility> BY_NAME;
private static final NameTagVisibility[] BY_ID;
static
{
NameTagVisibility[] values = NameTagVisibility.values();
ImmutableMap.Builder<String, NameTagVisibility> builder = ImmutableMap.builderWithExpectedSize( values.length );
for ( NameTagVisibility e : values )
{
builder.put( e.key, e );
}
BY_NAME = builder.build();
BY_ID = Arrays.copyOf( values, values.length - 1 ); // Ignore dummy UNKNOWN value
}
}
@Getter
@RequiredArgsConstructor
public enum CollisionRule
{
ALWAYS( "always" ),
NEVER( "never" ),
PUSH_OTHER_TEAMS( "pushOtherTeams" ),
PUSH_OWN_TEAM( "pushOwnTeam" );
//
private final String key;
//
private static final Map<String, CollisionRule> BY_NAME;
private static final CollisionRule[] BY_ID;
static
{
CollisionRule[] values = BY_ID = CollisionRule.values();
ImmutableMap.Builder<String, CollisionRule> builder = ImmutableMap.builderWithExpectedSize( values.length );
for ( CollisionRule e : values )
{
builder.put( e.key, e );
}
BY_NAME = builder.build();
}
}
}

View File

@ -2,9 +2,20 @@ package net.md_5.bungee.protocol;
import static org.junit.jupiter.api.Assertions.*;
import com.google.gson.Gson;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonPrimitive;
import org.junit.jupiter.api.Test;
import se.llbit.nbt.ByteArrayTag;
import se.llbit.nbt.CompoundTag;
import se.llbit.nbt.IntArrayTag;
import se.llbit.nbt.IntTag;
import se.llbit.nbt.ListTag;
import se.llbit.nbt.LongArrayTag;
import se.llbit.nbt.SpecificTag;
import se.llbit.nbt.StringTag;
import se.llbit.nbt.Tag;
public class TagUtilTest
{
@ -26,4 +37,190 @@ public class TagUtilTest
{
testDissembleReassemble( "{\"text\":\"\",\"extra\":[\"hello\",{\"text\":\"there\",\"color\":\"#ff0000\"},{\"text\":\"friend\",\"font\":\"minecraft:default\"}]}" );
}
public void testCreateMixedList(JsonArray array)
{
SpecificTag tag = TagUtil.fromJson( array );
assertInstanceOf( ListTag.class, tag );
ListTag list = (ListTag) tag;
assertEquals( SpecificTag.TAG_COMPOUND, list.getType() );
assertEquals( array.size(), list.size() );
for ( int i = 0; i < list.size(); i++ )
{
assertTrue( i < array.size() );
Tag element = list.get( i );
assertInstanceOf( CompoundTag.class, element );
CompoundTag compound = (CompoundTag) element;
JsonElement expected = array.get( i );
if ( expected instanceof JsonObject )
{
assertEquals( TagUtil.fromJson( expected ), compound );
} else
{
assertEquals( 1, compound.size() );
Tag value = compound.get( "" );
if ( expected instanceof JsonPrimitive )
{
JsonPrimitive primitive = (JsonPrimitive) expected;
if ( primitive.isNumber() )
{
Number number = primitive.getAsNumber();
if ( number instanceof Integer )
{
assertInstanceOf( IntTag.class, value );
IntTag integer = (IntTag) value;
assertEquals( array.get( i ).getAsInt(), integer.getData() );
}
} else if ( primitive.isString() )
{
assertInstanceOf( StringTag.class, value );
StringTag string = (StringTag) value;
assertEquals( array.get( i ).getAsString(), string.getData() );
}
}
}
}
}
@Test
public void testMixedListWithInt()
{
JsonArray array = new JsonArray();
array.add( 1 );
array.add( "a" );
testCreateMixedList( array );
}
@Test
public void testMixedListWithString()
{
JsonArray array = new JsonArray();
array.add( "a" );
array.add( 1L );
testCreateMixedList( array );
}
@Test
public void testMixedListWithObject()
{
JsonObject compound = new JsonObject();
compound.addProperty( "a", "b" );
JsonArray array = new JsonArray();
array.add( compound );
array.add( 1L );
testCreateMixedList( array );
}
@Test
public void testCreateEmptyList()
{
JsonArray array = new JsonArray();
SpecificTag tag = TagUtil.fromJson( array );
assertInstanceOf( ListTag.class, tag );
ListTag list = (ListTag) tag;
assertEquals( 0, list.size() );
assertEquals( Tag.TAG_END, list.getType() );
}
@Test
public void testCreateByteArray()
{
JsonArray array = new JsonArray();
array.add( ( (byte) 1 ) );
array.add( ( (byte) 2 ) );
SpecificTag tag = TagUtil.fromJson( array );
assertInstanceOf( ByteArrayTag.class, tag );
ByteArrayTag byteArray = (ByteArrayTag) tag;
assertEquals( 2, byteArray.getData().length );
for ( int i = 0; i < byteArray.getData().length; i++ )
{
assertTrue( i < array.size() );
byte item = byteArray.getData()[i];
assertEquals( array.get( i ).getAsByte(), item );
}
}
@Test
public void testCreateIntArray()
{
JsonArray array = new JsonArray();
array.add( 1 );
array.add( 2 );
SpecificTag tag = TagUtil.fromJson( array );
assertInstanceOf( IntArrayTag.class, tag );
IntArrayTag intArray = (IntArrayTag) tag;
assertEquals( 2, intArray.getData().length );
for ( int i = 0; i < intArray.getData().length; i++ )
{
assertTrue( i < array.size() );
int item = intArray.getData()[i];
assertEquals( array.get( i ).getAsInt(), item );
}
}
@Test
public void testCreateLongArray()
{
JsonArray array = new JsonArray();
array.add( 1L );
array.add( 2L );
SpecificTag tag = TagUtil.fromJson( array );
assertInstanceOf( LongArrayTag.class, tag );
LongArrayTag intArray = (LongArrayTag) tag;
assertEquals( 2, intArray.getData().length );
for ( int i = 0; i < intArray.getData().length; i++ )
{
assertTrue( i < array.size() );
long item = intArray.getData()[i];
assertEquals( array.get( i ).getAsLong(), item );
}
}
@Test
public void testCreateStringList()
{
JsonArray array = new JsonArray();
array.add( "a" );
array.add( "b" );
SpecificTag tag = TagUtil.fromJson( array );
assertInstanceOf( ListTag.class, tag );
ListTag list = (ListTag) tag;
assertEquals( SpecificTag.TAG_STRING, list.getType() );
assertEquals( 2, list.size() );
for ( int i = 0; i < list.size(); i++ )
{
assertTrue( i < array.size() );
Tag item = list.get( i );
assertInstanceOf( StringTag.class, item );
StringTag string = (StringTag) item;
assertEquals( array.get( i ).getAsString(), string.getData() );
}
}
}

View File

@ -9,8 +9,8 @@ public class PluginMessageTest
@Test
public void testModerniseChannel()
{
assertEquals( "bungeecord:main", PluginMessage.MODERNISE.apply( "BungeeCord" ) );
assertEquals( "BungeeCord", PluginMessage.MODERNISE.apply( "bungeecord:main" ) );
assertEquals( PluginMessage.BUNGEE_CHANNEL_MODERN, PluginMessage.MODERNISE.apply( PluginMessage.BUNGEE_CHANNEL_LEGACY ) );
assertEquals( PluginMessage.BUNGEE_CHANNEL_LEGACY, PluginMessage.MODERNISE.apply( PluginMessage.BUNGEE_CHANNEL_MODERN ) );
assertEquals( "legacy:foo", PluginMessage.MODERNISE.apply( "FoO" ) );
assertEquals( "foo:bar", PluginMessage.MODERNISE.apply( "foo:bar" ) );
}

View File

@ -4,15 +4,14 @@
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>net.md-5</groupId>
<groupId>fr.pandacube.bungeecord</groupId>
<artifactId>bungeecord-parent</artifactId>
<version>1.21-R0.1-SNAPSHOT</version>
<version>1.21-R0.3-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<groupId>net.md-5</groupId>
<artifactId>bungeecord-proxy</artifactId>
<version>1.21-R0.1-SNAPSHOT</version>
<version>1.21-R0.3-SNAPSHOT</version>
<packaging>jar</packaging>
<name>BungeeCord-Proxy</name>
@ -64,37 +63,37 @@
<classifier>linux-aarch_64</classifier>
</dependency>
<dependency>
<groupId>net.md-5</groupId>
<groupId>fr.pandacube.bungeecord</groupId>
<artifactId>bungeecord-api</artifactId>
<version>${project.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>net.md-5</groupId>
<groupId>fr.pandacube.bungeecord</groupId>
<artifactId>bungeecord-log</artifactId>
<version>${project.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>net.md-5</groupId>
<groupId>fr.pandacube.bungeecord</groupId>
<artifactId>bungeecord-native</artifactId>
<version>${project.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>net.md-5</groupId>
<groupId>fr.pandacube.bungeecord</groupId>
<artifactId>bungeecord-protocol</artifactId>
<version>${project.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>net.md-5</groupId>
<groupId>fr.pandacube.bungeecord</groupId>
<artifactId>bungeecord-query</artifactId>
<version>${project.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>net.md-5</groupId>
<groupId>fr.pandacube.bungeecord</groupId>
<artifactId>bungeecord-slf4j</artifactId>
<version>${project.version}</version>
<scope>compile</scope>

View File

@ -6,8 +6,6 @@ import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.Channel;
@ -52,31 +50,17 @@ import lombok.Getter;
import lombok.Setter;
import lombok.Synchronized;
import net.md_5.bungee.api.CommandSender;
import net.md_5.bungee.api.Favicon;
import net.md_5.bungee.api.ProxyServer;
import net.md_5.bungee.api.ReconnectHandler;
import net.md_5.bungee.api.ServerPing;
import net.md_5.bungee.api.Title;
import net.md_5.bungee.api.chat.BaseComponent;
import net.md_5.bungee.api.chat.ComponentStyle;
import net.md_5.bungee.api.chat.KeybindComponent;
import net.md_5.bungee.api.chat.ScoreComponent;
import net.md_5.bungee.api.chat.SelectorComponent;
import net.md_5.bungee.api.chat.TextComponent;
import net.md_5.bungee.api.chat.TranslatableComponent;
import net.md_5.bungee.api.config.ConfigurationAdapter;
import net.md_5.bungee.api.config.ListenerInfo;
import net.md_5.bungee.api.config.ServerInfo;
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.chat.ComponentSerializer;
import net.md_5.bungee.chat.ComponentStyleSerializer;
import net.md_5.bungee.chat.KeybindComponentSerializer;
import net.md_5.bungee.chat.ScoreComponentSerializer;
import net.md_5.bungee.chat.SelectorComponentSerializer;
import net.md_5.bungee.chat.TextComponentSerializer;
import net.md_5.bungee.chat.TranslatableComponentSerializer;
import net.md_5.bungee.command.CommandBungee;
import net.md_5.bungee.command.CommandEnd;
import net.md_5.bungee.command.CommandIP;
@ -91,10 +75,10 @@ import net.md_5.bungee.forge.ForgeConstants;
import net.md_5.bungee.log.BungeeLogger;
import net.md_5.bungee.log.LoggingForwardHandler;
import net.md_5.bungee.log.LoggingOutputStream;
import net.md_5.bungee.module.ModuleManager;
import net.md_5.bungee.netty.PipelineUtils;
import net.md_5.bungee.protocol.DefinedPacket;
import net.md_5.bungee.protocol.ProtocolConstants;
import net.md_5.bungee.protocol.channel.BungeeChannelInitializer;
import net.md_5.bungee.protocol.packet.PluginMessage;
import net.md_5.bungee.query.RemoteQuery;
import net.md_5.bungee.scheduler.BungeeScheduler;
@ -164,23 +148,12 @@ public class BungeeCord extends ProxyServer
private final ConsoleReader consoleReader;
@Getter
private final Logger logger;
public final Gson gson = new GsonBuilder()
.registerTypeAdapter( BaseComponent.class, new ComponentSerializer() )
.registerTypeAdapter( TextComponent.class, new TextComponentSerializer() )
.registerTypeAdapter( TranslatableComponent.class, new TranslatableComponentSerializer() )
.registerTypeAdapter( KeybindComponent.class, new KeybindComponentSerializer() )
.registerTypeAdapter( ScoreComponent.class, new ScoreComponentSerializer() )
.registerTypeAdapter( SelectorComponent.class, new SelectorComponentSerializer() )
.registerTypeAdapter( ComponentStyle.class, new ComponentStyleSerializer() )
.registerTypeAdapter( ServerPing.PlayerInfo.class, new PlayerInfoSerializer() )
.registerTypeAdapter( Favicon.class, Favicon.getFaviconTypeAdapter() ).create();
@Getter
private ConnectionThrottle connectionThrottle;
private final ModuleManager moduleManager = new ModuleManager();
{
// TODO: Proper fallback when we interface the manager
registerChannel( "BungeeCord" );
registerChannel( PluginMessage.BUNGEE_CHANNEL_LEGACY );
}
public static BungeeCord getInstance()
@ -188,6 +161,21 @@ public class BungeeCord extends ProxyServer
return (BungeeCord) ProxyServer.getInstance();
}
private final Unsafe unsafe = new Unsafe()
{
@Getter
@Setter
private BungeeChannelInitializer frontendChannelInitializer;
@Getter
@Setter
private BungeeChannelInitializer backendChannelInitializer;
@Getter
@Setter
private BungeeChannelInitializer serverInfoChannelInitializer;
};
@SuppressFBWarnings("DM_DEFAULT_ENCODING")
public BungeeCord() throws IOException
{
@ -274,10 +262,6 @@ public class BungeeCord extends ProxyServer
eventLoops = PipelineUtils.newEventLoopGroup( 0, new ThreadFactoryBuilder().setNameFormat( "Netty IO Thread #%1$d" ).build() );
File moduleDirectory = new File( "modules" );
moduleManager.load( this, moduleDirectory );
pluginManager.detectPlugins( moduleDirectory );
pluginsFolder.mkdir();
pluginManager.detectPlugins( pluginsFolder );
@ -360,7 +344,7 @@ public class BungeeCord extends ProxyServer
.channel( PipelineUtils.getServerChannel( info.getSocketAddress() ) )
.option( ChannelOption.SO_REUSEADDR, true ) // TODO: Move this elsewhere!
.childAttr( PipelineUtils.LISTENER, info )
.childHandler( PipelineUtils.SERVER_CHILD )
.childHandler( unsafe().getFrontendChannelInitializer().getChannelInitializer() )
.group( eventLoops )
.localAddress( info.getSocketAddress() )
.bind().addListener( listener );
@ -831,4 +815,10 @@ public class BungeeCord extends ProxyServer
{
return new BungeeTitle();
}
@Override
public Unsafe unsafe()
{
return unsafe;
}
}

View File

@ -5,7 +5,6 @@ import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Date;
import java.util.concurrent.TimeUnit;
import joptsimple.OptionParser;
import joptsimple.OptionSet;
import net.md_5.bungee.api.ChatColor;
@ -56,8 +55,7 @@ public class BungeeCordLauncher
System.err.println( "*** Warning, this build is outdated ***" );
System.err.println( "*** Please download a new build from http://ci.md-5.net/job/BungeeCord ***" );
System.err.println( "*** You will get NO support regarding this build ***" );
System.err.println( "*** Server will start in 10 seconds ***" );
Thread.sleep( TimeUnit.SECONDS.toMillis( 10 ) );
}
}

View File

@ -186,7 +186,7 @@ public class BungeeServerInfo implements ServerInfo
new Bootstrap()
.channel( PipelineUtils.getChannel( socketAddress ) )
.group( BungeeCord.getInstance().eventLoops )
.handler( PipelineUtils.BASE_SERVERSIDE )
.handler( ProxyServer.getInstance().unsafe().getServerInfoChannelInitializer().getChannelInitializer() )
.option( ChannelOption.CONNECT_TIMEOUT_MILLIS, BungeeCord.getInstance().getConfig().getRemotePingTimeout() )
.remoteAddress( socketAddress )
.connect()

View File

@ -156,7 +156,7 @@ public class BungeeTitle implements Title
{
if ( player.getPendingConnection().getVersion() >= ProtocolConstants.MINECRAFT_1_17 )
{
( (UserConnection) player ).sendPacketQueued( packet.newPacket );
player.unsafe().sendPacketQueued( packet.newPacket );
} else
{
player.unsafe().sendPacket( packet.oldPacket );

View File

@ -14,6 +14,7 @@ import net.md_5.bungee.api.connection.Server;
import net.md_5.bungee.netty.ChannelWrapper;
import net.md_5.bungee.protocol.DefinedPacket;
import net.md_5.bungee.protocol.Protocol;
import net.md_5.bungee.protocol.ProtocolConstants;
import net.md_5.bungee.protocol.packet.PluginMessage;
@RequiredArgsConstructor
@ -40,6 +41,18 @@ public class ServerConnection implements Server
{
ch.write( packet );
}
@Override
public void sendPacketQueued(DefinedPacket packet)
{
if ( ch.getEncodeVersion() >= ProtocolConstants.MINECRAFT_1_20_2 )
{
ServerConnection.this.sendPacketQueued( packet );
} else
{
sendPacket( packet );
}
}
};
public void sendPacketQueued(DefinedPacket packet)
@ -53,6 +66,8 @@ public class ServerConnection implements Server
Protocol encodeProtocol = ch.getEncodeProtocol();
if ( !encodeProtocol.TO_SERVER.hasPacket( packet.getClass(), ch.getEncodeVersion() ) )
{
// we should limit this so bad api usage won't oom the server.
Preconditions.checkState( packetQueue.size() <= 4096, "too many queued packets" );
packetQueue.add( packet );
} else
{

View File

@ -25,7 +25,6 @@ import net.md_5.bungee.api.score.Objective;
import net.md_5.bungee.api.score.Score;
import net.md_5.bungee.api.score.Scoreboard;
import net.md_5.bungee.api.score.Team;
import net.md_5.bungee.chat.ComponentSerializer;
import net.md_5.bungee.connection.CancelSendSignal;
import net.md_5.bungee.connection.DownstreamBridge;
import net.md_5.bungee.connection.LoginResult;
@ -104,6 +103,7 @@ public class ServerConnector extends PacketHandler
@Override
public void connected(ChannelWrapper channel) throws Exception
{
channel.setVersion( user.getPendingConnection().getVersion() );
this.ch = channel;
this.handshakeHandler = new ForgeServerHandler( user, ch, target );
@ -117,7 +117,7 @@ public class ServerConnector extends PacketHandler
LoginResult profile = user.getPendingConnection().getLoginProfile();
if ( profile != null && profile.getProperties() != null && profile.getProperties().length > 0 )
{
newHost += "\00" + BungeeCord.getInstance().gson.toJson( profile.getProperties() );
newHost += "\00" + LoginResult.GSON.toJson( profile.getProperties() );
}
copiedHandshake.setHost( newHost );
} else if ( !user.getExtraDataInHandshake().isEmpty() )
@ -301,7 +301,7 @@ public class ServerConnector extends PacketHandler
{
user.unsafe().sendPacket( new ScoreboardObjective(
objective.getName(),
( user.getPendingConnection().getVersion() >= ProtocolConstants.MINECRAFT_1_13 ) ? Either.right( ComponentSerializer.deserialize( objective.getValue() ) ) : Either.left( objective.getValue() ),
( user.getPendingConnection().getVersion() >= ProtocolConstants.MINECRAFT_1_13 ) ? Either.right( user.getChatSerializer().deserialize( objective.getValue() ) ) : Either.left( objective.getValue() ),
ScoreboardObjective.HealthDisplay.fromString( objective.getType() ),
(byte) 1, null )
);

View File

@ -3,10 +3,8 @@ package net.md_5.bungee;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableMap;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.util.internal.PlatformDependent;
import java.net.InetSocketAddress;
@ -40,7 +38,7 @@ import net.md_5.bungee.api.connection.ProxiedPlayer;
import net.md_5.bungee.api.event.PermissionCheckEvent;
import net.md_5.bungee.api.event.ServerConnectEvent;
import net.md_5.bungee.api.score.Scoreboard;
import net.md_5.bungee.chat.ComponentSerializer;
import net.md_5.bungee.chat.VersionedComponentSerializer;
import net.md_5.bungee.connection.InitialHandler;
import net.md_5.bungee.entitymap.EntityMap;
import net.md_5.bungee.forge.ForgeClientHandler;
@ -49,9 +47,8 @@ import net.md_5.bungee.forge.ForgeServerHandler;
import net.md_5.bungee.netty.ChannelWrapper;
import net.md_5.bungee.netty.HandlerBoss;
import net.md_5.bungee.netty.PipelineUtils;
import net.md_5.bungee.protocol.ChatSerializer;
import net.md_5.bungee.protocol.DefinedPacket;
import net.md_5.bungee.protocol.MinecraftDecoder;
import net.md_5.bungee.protocol.MinecraftEncoder;
import net.md_5.bungee.protocol.PacketWrapper;
import net.md_5.bungee.protocol.Protocol;
import net.md_5.bungee.protocol.ProtocolConstants;
@ -137,6 +134,8 @@ public final class UserConnection implements ProxiedPlayer
private String displayName;
@Getter
private EntityMap entityRewrite;
@Getter
private VersionedComponentSerializer chatSerializer;
private Locale locale;
/*========================================================================*/
@Getter
@ -154,11 +153,24 @@ public final class UserConnection implements ProxiedPlayer
{
ch.write( packet );
}
@Override
public void sendPacketQueued(DefinedPacket packet)
{
if ( pendingConnection.getVersion() >= ProtocolConstants.MINECRAFT_1_20_2 )
{
UserConnection.this.sendPacketQueued( packet );
} else
{
sendPacket( packet );
}
}
};
public boolean init()
{
this.entityRewrite = EntityMap.getEntityMap( getPendingConnection().getVersion() );
this.chatSerializer = ChatSerializer.forVersion( getPendingConnection().getVersion() );
this.displayName = name;
@ -195,6 +207,8 @@ public final class UserConnection implements ProxiedPlayer
Protocol encodeProtocol = ch.getEncodeProtocol();
if ( !encodeProtocol.TO_CLIENT.hasPacket( packet.getClass(), getPendingConnection().getVersion() ) )
{
// we should limit this so bad api usage won't oom the server.
Preconditions.checkState( packetQueue.size() <= 4096, "too many queued packets" );
packetQueue.add( packet );
} else
{
@ -362,17 +376,6 @@ public final class UserConnection implements ProxiedPlayer
pendingConnects.add( target );
ChannelInitializer initializer = new ChannelInitializer()
{
@Override
protected void initChannel(Channel ch) throws Exception
{
PipelineUtils.BASE_SERVERSIDE.initChannel( ch );
ch.pipeline().addAfter( PipelineUtils.FRAME_DECODER, PipelineUtils.PACKET_DECODER, new MinecraftDecoder( Protocol.HANDSHAKE, false, getPendingConnection().getVersion() ) );
ch.pipeline().addAfter( PipelineUtils.FRAME_PREPENDER, PipelineUtils.PACKET_ENCODER, new MinecraftEncoder( Protocol.HANDSHAKE, false, getPendingConnection().getVersion() ) );
ch.pipeline().get( HandlerBoss.class ).setHandler( new ServerConnector( bungee, UserConnection.this, target ) );
}
};
ChannelFutureListener listener = new ChannelFutureListener()
{
@Override
@ -401,13 +404,16 @@ public final class UserConnection implements ProxiedPlayer
{
sendMessage( bungee.getTranslation( "fallback_kick", connectionFailMessage( future.cause() ) ) );
}
} else
{
future.channel().pipeline().get( HandlerBoss.class ).setHandler( new ServerConnector( bungee, UserConnection.this, target ) );
}
}
};
Bootstrap b = new Bootstrap()
.channel( PipelineUtils.getChannel( target.getAddress() ) )
.group( ch.getHandle().eventLoop() )
.handler( initializer )
.handler( bungee.unsafe().getBackendChannelInitializer().getChannelInitializer() )
.option( ChannelOption.CONNECT_TIMEOUT_MILLIS, request.getConnectTimeout() )
.remoteAddress( target.getAddress() );
// Windows is bugged, multi homed users will just have to live with random connecting IPs
@ -554,7 +560,7 @@ public final class UserConnection implements ProxiedPlayer
sendPacketQueued( new SystemChat( message, position.ordinal() ) );
} else
{
sendPacketQueued( new Chat( ComponentSerializer.toString( message ), (byte) position.ordinal(), sender ) );
sendPacketQueued( new Chat( chatSerializer.toString( message ), (byte) position.ordinal(), sender ) );
}
}

View File

@ -1,57 +0,0 @@
package net.md_5.bungee.compress;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToMessageEncoder;
import java.util.List;
import java.util.zip.Deflater;
import lombok.Getter;
import lombok.Setter;
import net.md_5.bungee.jni.zlib.BungeeZlib;
import net.md_5.bungee.protocol.DefinedPacket;
public class PacketCompressor extends MessageToMessageEncoder<ByteBuf>
{
@Getter
private final BungeeZlib zlib = CompressFactory.zlib.newInstance();
@Setter
private int threshold = 256;
@Setter
private boolean compose = true;
@Override
public void handlerAdded(ChannelHandlerContext ctx) throws Exception
{
zlib.init( true, Deflater.DEFAULT_COMPRESSION );
}
@Override
public void handlerRemoved(ChannelHandlerContext ctx) throws Exception
{
zlib.free();
}
@Override
protected void encode(ChannelHandlerContext ctx, ByteBuf msg, List<Object> out) throws Exception
{
int origSize = msg.readableBytes();
if ( origSize < threshold )
{
if ( compose )
{
// create a virtual buffer to avoid copying of data
out.add( ctx.alloc().compositeDirectBuffer( 2 ).addComponents( true, ctx.alloc().directBuffer( 1 ).writeByte( 0 ), msg.retain() ) );
} else
{
out.add( ctx.alloc().directBuffer( origSize + 1 ).writeByte( 0 ).writeBytes( msg ) );
}
} else
{
ByteBuf buf = ctx.alloc().directBuffer( BungeeZlib.OUTPUT_BUFFER_SIZE );
DefinedPacket.writeVarInt( origSize, buf );
zlib.process( msg, buf );
out.add( buf );
}
}
}

View File

@ -7,10 +7,12 @@ import io.netty.handler.codec.MessageToMessageDecoder;
import java.util.List;
import net.md_5.bungee.jni.zlib.BungeeZlib;
import net.md_5.bungee.protocol.DefinedPacket;
import net.md_5.bungee.protocol.OverflowPacketException;
public class PacketDecompressor extends MessageToMessageDecoder<ByteBuf>
{
private static final int MAX_DECOMPRESSED_LEN = 1 << 23;
private final BungeeZlib zlib = CompressFactory.zlib.newInstance();
@Override
@ -34,8 +36,14 @@ public class PacketDecompressor extends MessageToMessageDecoder<ByteBuf>
out.add( in.retain() );
} else
{
ByteBuf decompressed = ctx.alloc().directBuffer();
if ( size > MAX_DECOMPRESSED_LEN )
{
throw new OverflowPacketException( "Packet may not be larger than " + MAX_DECOMPRESSED_LEN + " bytes" );
}
// Do not use size as max capacity, as its possible that the entity rewriter increases the size afterwards
// This would result in a kick (it happens rarely as the entity ids size must differ)
ByteBuf decompressed = ctx.alloc().directBuffer( size, MAX_DECOMPRESSED_LEN );
try
{
zlib.process( in, decompressed );

View File

@ -69,6 +69,8 @@ public class Configuration implements ProxyConfig
private boolean preventProxyConnections;
private boolean forgeSupport;
private boolean rejectTransfers;
private int maxPacketsPerSecond = 1 << 12;
private int maxPacketDataPerSecond = 1 << 25;
public void load()
{
@ -105,6 +107,8 @@ public class Configuration implements ProxyConfig
preventProxyConnections = adapter.getBoolean( "prevent_proxy_connections", preventProxyConnections );
forgeSupport = adapter.getBoolean( "forge_support", forgeSupport );
rejectTransfers = adapter.getBoolean( "reject_transfers", rejectTransfers );
maxPacketsPerSecond = adapter.getInt( "max_packets_per_second", maxPacketsPerSecond );
maxPacketDataPerSecond = adapter.getInt( "max_packets_data_per_second", maxPacketDataPerSecond );
disabledCommands = new CaseInsensitiveSet( (Collection<String>) adapter.getList( "disabled_commands", Arrays.asList( "disabledcommandhere" ) ) );

View File

@ -189,7 +189,9 @@ public class YamlConfig implements ConfigurationAdapter
@Override
public int getInt(String path, int def)
{
return get( path, def );
// #3791: Sometimes third-party tools rewrite large ints into doubles
Number number = get( path, def );
return number.intValue();
}
@Override

View File

@ -6,6 +6,7 @@ import com.google.common.collect.Lists;
import com.google.common.io.ByteArrayDataOutput;
import com.google.common.io.ByteStreams;
import com.mojang.brigadier.arguments.StringArgumentType;
import com.mojang.brigadier.builder.ArgumentBuilder;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import com.mojang.brigadier.builder.RequiredArgumentBuilder;
import com.mojang.brigadier.context.StringRange;
@ -19,9 +20,12 @@ import io.netty.channel.unix.DomainSocketAddress;
import java.io.DataInput;
import java.net.InetSocketAddress;
import java.util.ArrayList;
import java.util.Collection;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.logging.Level;
import java.util.stream.Collectors;
import lombok.RequiredArgsConstructor;
import net.md_5.bungee.ServerConnection;
@ -35,6 +39,7 @@ import net.md_5.bungee.api.chat.TextComponent;
import net.md_5.bungee.api.config.ServerInfo;
import net.md_5.bungee.api.connection.ProxiedPlayer;
import net.md_5.bungee.api.connection.Server;
import net.md_5.bungee.api.event.CommandsDeclareEvent;
import net.md_5.bungee.api.event.PluginMessageEvent;
import net.md_5.bungee.api.event.ServerConnectEvent;
import net.md_5.bungee.api.event.ServerDisconnectEvent;
@ -46,7 +51,6 @@ import net.md_5.bungee.api.score.Position;
import net.md_5.bungee.api.score.Score;
import net.md_5.bungee.api.score.Scoreboard;
import net.md_5.bungee.api.score.Team;
import net.md_5.bungee.chat.ComponentSerializer;
import net.md_5.bungee.entitymap.EntityMap;
import net.md_5.bungee.netty.ChannelWrapper;
import net.md_5.bungee.netty.PacketHandler;
@ -196,7 +200,7 @@ public class DownstreamBridge extends PacketHandler
switch ( objective.getAction() )
{
case 0:
serverScoreboard.addObjective( new Objective( objective.getName(), ( objective.getValue().isLeft() ) ? objective.getValue().getLeft() : ComponentSerializer.toString( objective.getValue().getRight() ), objective.getType().toString() ) );
serverScoreboard.addObjective( new Objective( objective.getName(), ( objective.getValue().isLeft() ) ? objective.getValue().getLeft() : con.getChatSerializer().toString( objective.getValue().getRight() ), objective.getType().toString() ) );
break;
case 1:
serverScoreboard.removeObjective( objective.getName() );
@ -205,7 +209,7 @@ public class DownstreamBridge extends PacketHandler
Objective oldObjective = serverScoreboard.getObjective( objective.getName() );
if ( oldObjective != null )
{
oldObjective.setValue( ( objective.getValue().isLeft() ) ? objective.getValue().getLeft() : ComponentSerializer.toString( objective.getValue().getRight() ) );
oldObjective.setValue( ( objective.getValue().isLeft() ) ? objective.getValue().getLeft() : con.getChatSerializer().toString( objective.getValue().getRight() ) );
oldObjective.setType( objective.getType().toString() );
}
break;
@ -279,13 +283,16 @@ public class DownstreamBridge extends PacketHandler
{
if ( team.getMode() == 0 || team.getMode() == 2 )
{
t.setDisplayName( team.getDisplayName().getLeftOrCompute( ComponentSerializer::toString ) );
t.setPrefix( team.getPrefix().getLeftOrCompute( ComponentSerializer::toString ) );
t.setSuffix( team.getSuffix().getLeftOrCompute( ComponentSerializer::toString ) );
t.setDisplayName( team.getDisplayName().getLeftOrCompute( (component) -> con.getChatSerializer().toString( component ) ) );
t.setPrefix( team.getPrefix().getLeftOrCompute( (component) -> con.getChatSerializer().toString( component ) ) );
t.setSuffix( team.getSuffix().getLeftOrCompute( (component) -> con.getChatSerializer().toString( component ) ) );
t.setFriendlyFire( team.getFriendlyFire() );
t.setNameTagVisibility( team.getNameTagVisibility() );
t.setCollisionRule( team.getCollisionRule() );
t.setNameTagVisibility( team.getNameTagVisibility().getKey() );
t.setColor( team.getColor() );
if ( team.getCollisionRule() != null )
{
t.setCollisionRule( team.getCollisionRule().getKey() );
}
}
if ( team.getPlayers() != null )
{
@ -324,7 +331,7 @@ public class DownstreamBridge extends PacketHandler
Preconditions.checkState( !serverBrand.contains( bungee.getName() ), "Cannot connect proxy to itself!" );
brand = ByteBufAllocator.DEFAULT.heapBuffer();
DefinedPacket.writeString( bungee.getName() + " (" + bungee.getVersion() + ")" + " <- " + serverBrand, brand );
DefinedPacket.writeString( bungee.getName() + " (" + bungee.getVersion() + ")" + " <- " + server.getInfo().getName() + " (" + serverBrand + ")", brand );
pluginMessage.setData( DefinedPacket.toArray( brand ) );
brand.release();
// changes in the packet are ignored so we need to send it manually
@ -332,7 +339,7 @@ public class DownstreamBridge extends PacketHandler
throw CancelSendSignal.INSTANCE;
}
if ( pluginMessage.getTag().equals( "BungeeCord" ) )
if ( pluginMessage.getTag().equals( PluginMessage.BUNGEE_CHANNEL_LEGACY ) )
{
ByteArrayDataOutput out = ByteStreams.newDataOutput();
String subChannel = in.readUTF();
@ -356,7 +363,7 @@ public class DownstreamBridge extends PacketHandler
out.write( data );
byte[] payload = out.toByteArray();
target.getServer().sendData( "BungeeCord", payload );
target.getServer().sendData( PluginMessage.BUNGEE_CHANNEL_LEGACY, payload );
}
// Null out stream, important as we don't want to send to ourselves
@ -388,7 +395,7 @@ public class DownstreamBridge extends PacketHandler
{
if ( server != this.server.getInfo() )
{
server.sendData( "BungeeCord", payload );
server.sendData( PluginMessage.BUNGEE_CHANNEL_LEGACY, payload );
}
}
break;
@ -397,7 +404,7 @@ public class DownstreamBridge extends PacketHandler
{
if ( server != this.server.getInfo() )
{
server.sendData( "BungeeCord", payload, false );
server.sendData( PluginMessage.BUNGEE_CHANNEL_LEGACY, payload, false );
}
}
break;
@ -405,7 +412,7 @@ public class DownstreamBridge extends PacketHandler
ServerInfo server = bungee.getServerInfo( target );
if ( server != null )
{
server.sendData( "BungeeCord", payload );
server.sendData( PluginMessage.BUNGEE_CHANNEL_LEGACY, payload );
}
break;
}
@ -553,7 +560,7 @@ public class DownstreamBridge extends PacketHandler
case "MessageRaw":
{
String target = in.readUTF();
BaseComponent[] message = ComponentSerializer.parse( in.readUTF() );
BaseComponent[] message = con.getChatSerializer().parse( in.readUTF() );
if ( target.equals( "ALL" ) )
{
for ( ProxiedPlayer player : bungee.getPlayers() )
@ -620,7 +627,7 @@ public class DownstreamBridge extends PacketHandler
ProxiedPlayer player = bungee.getPlayer( in.readUTF() );
if ( player != null )
{
BaseComponent[] kickReason = ComponentSerializer.parse( in.readUTF() );
BaseComponent[] kickReason = con.getChatSerializer().parse( in.readUTF() );
player.disconnect( kickReason );
}
break;
@ -633,7 +640,7 @@ public class DownstreamBridge extends PacketHandler
byte[] b = out.toByteArray();
if ( b.length != 0 )
{
server.sendData( "BungeeCord", b );
server.sendData( PluginMessage.BUNGEE_CHANNEL_LEGACY, b );
}
}
@ -757,6 +764,11 @@ public class DownstreamBridge extends PacketHandler
{
boolean modified = false;
CommandsDeclareEvent commandsDeclareEvent = new CommandsDeclareEvent( server, con, commands.getRoot() );
bungee.getPluginManager().callEvent( commandsDeclareEvent );
modified = commandsDeclareEvent.isModified();
for ( Map.Entry<String, Command> command : bungee.getPluginManager().getCommands() )
{
if ( !bungee.getDisabledCommands().contains( command.getKey() ) && commands.getRoot().getChild( command.getKey() ) == null && command.getValue().hasPermission( con ) )
@ -773,11 +785,65 @@ public class DownstreamBridge extends PacketHandler
if ( modified )
{
commands.setRoot( (com.mojang.brigadier.tree.RootCommandNode) filterCommandNode( commands.getRoot(), new IdentityHashMap<>() ) );
con.unsafe().sendPacket( commands );
throw CancelSendSignal.INSTANCE;
}
}
/*
* Create a deep copy of the provided command node but removes any node that are not accessible by the player
* (using {@link CommandNode#getRequirement()})
*/
private CommandNode filterCommandNode(CommandNode source, IdentityHashMap<CommandNode, CommandNode> commandNodeMapping)
{
CommandNode dest;
if ( source instanceof com.mojang.brigadier.tree.RootCommandNode )
{
dest = new com.mojang.brigadier.tree.RootCommandNode();
} else
{
if ( source.getRequirement() != null )
{
try
{
if ( !source.getRequirement().test( con ) )
{
commandNodeMapping.put( source, null );
return null;
}
} catch ( Throwable t )
{
ProxyServer.getInstance().getLogger().log( Level.SEVERE, "Requirement test for command node " + source + " encountered an exception", t );
}
}
ArgumentBuilder destChildBuilder = source.createBuilder();
destChildBuilder.requires( sender -> true );
if ( destChildBuilder.getRedirect() != null )
{
if ( commandNodeMapping.containsKey( destChildBuilder.getRedirect() ) )
destChildBuilder.redirect( commandNodeMapping.get( destChildBuilder.getRedirect() ) );
else
destChildBuilder.redirect( filterCommandNode( destChildBuilder.getRedirect(), commandNodeMapping ) );
}
dest = destChildBuilder.build();
}
commandNodeMapping.put( source, dest );
for ( CommandNode sourceChild : (Collection<CommandNode>) source.getChildren() )
{
CommandNode destChild = filterCommandNode( sourceChild, commandNodeMapping );
if ( destChild == null )
continue;
dest.addChild( destChild );
}
return dest;
}
@Override
public void handle(ServerData serverData) throws Exception
{

View File

@ -2,6 +2,7 @@ package net.md_5.bungee.connection;
import com.google.common.base.Preconditions;
import com.google.gson.Gson;
import io.netty.channel.EventLoop;
import java.math.BigInteger;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
@ -121,6 +122,12 @@ public class InitialHandler extends PacketHandler implements PendingConnection
{
ch.write( packet );
}
@Override
public void sendPacketQueued(DefinedPacket packet)
{
throw new UnsupportedOperationException( "Not supported" );
}
};
@Getter
private boolean onlineMode = BungeeCord.getInstance().config.isOnlineMode();
@ -143,6 +150,15 @@ public class InitialHandler extends PacketHandler implements PendingConnection
private boolean transferred;
private UserConnection userCon;
@Getter
private boolean duplication = false;
@Getter
private String realName = null;
@Getter
private UUID realId = null;
@Override
public boolean shouldHandle(PacketWrapper packet) throws Exception
{
@ -187,12 +203,6 @@ public class InitialHandler extends PacketHandler implements PendingConnection
}
}
@Override
public void handle(PluginMessage pluginMessage) throws Exception
{
this.relayMessage( pluginMessage );
}
@Override
public void handle(LegacyHandshake legacyHandshake) throws Exception
{
@ -228,11 +238,6 @@ public class InitialHandler extends PacketHandler implements PendingConnection
@Override
public void done(ProxyPingEvent result, Throwable error)
{
if ( ch.isClosing() )
{
return;
}
ServerPing legacy = result.getResponse();
String kickMessage;
@ -256,7 +261,7 @@ public class InitialHandler extends PacketHandler implements PendingConnection
}
};
bungee.getPluginManager().callEvent( new ProxyPingEvent( InitialHandler.this, result, callback ) );
bungee.getPluginManager().callEvent( new ProxyPingEvent( InitialHandler.this, result, eventLoopCallback( callback ) ) );
}
};
@ -309,7 +314,7 @@ public class InitialHandler extends PacketHandler implements PendingConnection
@Override
public void done(ProxyPingEvent pingResult, Throwable error)
{
Gson gson = BungeeCord.getInstance().gson;
Gson gson = PingHandler.gson;
unsafe.sendPacket( new StatusResponse( gson.toJson( pingResult.getResponse() ) ) );
if ( bungee.getConnectionThrottle() != null )
{
@ -318,7 +323,7 @@ public class InitialHandler extends PacketHandler implements PendingConnection
}
};
bungee.getPluginManager().callEvent( new ProxyPingEvent( InitialHandler.this, result, callback ) );
bungee.getPluginManager().callEvent( new ProxyPingEvent( InitialHandler.this, result, eventLoopCallback( callback ) ) );
}
};
@ -456,6 +461,7 @@ public class InitialHandler extends PacketHandler implements PendingConnection
}
this.loginRequest = loginRequest;
setName( realName = loginRequest.getData() );
int limit = BungeeCord.getInstance().config.getPlayerLimit();
if ( limit > 0 && bungee.getOnlineCount() >= limit )
@ -464,14 +470,6 @@ public class InitialHandler extends PacketHandler implements PendingConnection
return;
}
// If offline mode and they are already on, don't allow connect
// We can just check by UUID here as names are based on UUID
if ( !isOnlineMode() && bungee.getPlayer( getUniqueId() ) != null )
{
disconnect( bungee.getTranslation( "already_connected_proxy" ) );
return;
}
Callback<PreLoginEvent> callback = new Callback<PreLoginEvent>()
{
@ -484,9 +482,15 @@ public class InitialHandler extends PacketHandler implements PendingConnection
disconnect( ( reason != null ) ? reason : TextComponent.fromLegacy( bungee.getTranslation( "kick_message" ) ) );
return;
}
if ( ch.isClosing() )
if ( !realName.equals( name ) )
{
return;
// Floodgate changes the name attribute with reflexion
setName( realName = name );
}
if ( uniqueId != null )
{
// if plugin called setUniqueId()
realId = uniqueId;
}
if ( onlineMode )
{
@ -501,7 +505,7 @@ public class InitialHandler extends PacketHandler implements PendingConnection
};
// fire pre login event
bungee.getPluginManager().callEvent( new PreLoginEvent( InitialHandler.this, callback ) );
bungee.getPluginManager().callEvent( new PreLoginEvent( InitialHandler.this, eventLoopCallback( callback ) ) );
}
@Override
@ -514,7 +518,7 @@ public class InitialHandler extends PacketHandler implements PendingConnection
BungeeCipher decrypt = EncryptionUtil.getCipher( false, sharedKey );
ch.addBefore( PipelineUtils.FRAME_DECODER, PipelineUtils.DECRYPT_HANDLER, new CipherDecoder( decrypt ) );
BungeeCipher encrypt = EncryptionUtil.getCipher( true, sharedKey );
ch.addBefore( PipelineUtils.FRAME_PREPENDER, PipelineUtils.ENCRYPT_HANDLER, new CipherEncoder( encrypt ) );
ch.addBefore( PipelineUtils.FRAME_PREPENDER_AND_COMPRESS, PipelineUtils.ENCRYPT_HANDLER, new CipherEncoder( encrypt ) );
// disable use of composite buffers if we use natives
ch.updateComposite();
@ -540,12 +544,12 @@ public class InitialHandler extends PacketHandler implements PendingConnection
{
if ( error == null )
{
LoginResult obj = BungeeCord.getInstance().gson.fromJson( result, LoginResult.class );
LoginResult obj = LoginResult.GSON.fromJson( result, LoginResult.class );
if ( obj != null && obj.getId() != null )
{
loginProfile = obj;
name = obj.getName();
uniqueId = Util.getUUID( obj.getId() );
setName( realName = obj.getName() );
uniqueId = realId = Util.getUUID( obj.getId() );
finish();
return;
}
@ -563,10 +567,25 @@ public class InitialHandler extends PacketHandler implements PendingConnection
private void finish()
{
offlineId = UUID.nameUUIDFromBytes( ( "OfflinePlayer:" + getName() ).getBytes( StandardCharsets.UTF_8 ) );
if ( uniqueId == null )
if ( uniqueId == null ) // offline mode and no plugin used setUniqueId()
{
uniqueId = offlineId;
uniqueId = realId = offlineId;
}
/*
* At this point, player is either authenticated by Mojang (online mode),
* by a plugin (Floodgate ?) or the offline id is set.
*/
ProxiedPlayer existingPlayer = bungee.getPlayer( uniqueId );
if ( existingPlayer != null && existingPlayer.hasPermission( "bungeecord.multiple_connect" ) )
{
UUID newId = generateDuplicatedId( uniqueId );
if ( !uniqueId.equals( newId ) )
{
uniqueId = newId;
setName( name + "." + getDuplicationIndex( newId ) );
duplication = true;
}
}
rewriteId = ( bungee.config.isIpForward() ) ? uniqueId : offlineId;
@ -621,18 +640,7 @@ public class InitialHandler extends PacketHandler implements PendingConnection
disconnect( ( reason != null ) ? reason : TextComponent.fromLegacy( bungee.getTranslation( "kick_message" ) ) );
return;
}
if ( ch.isClosing() )
{
return;
}
ch.getHandle().eventLoop().execute( new Runnable()
{
@Override
public void run()
{
if ( !ch.isClosing() )
{
userCon = new UserConnection( bungee, ch, getName(), InitialHandler.this );
userCon.setCompressionThreshold( BungeeCord.getInstance().config.getCompressionThreshold() );
@ -643,13 +651,10 @@ public class InitialHandler extends PacketHandler implements PendingConnection
}
finish2();
}
}
} );
}
};
// fire login event
bungee.getPluginManager().callEvent( new LoginEvent( InitialHandler.this, complete ) );
bungee.getPluginManager().callEvent( new LoginEvent( InitialHandler.this, eventLoopCallback( complete ) ) );
}
private void finish2()
@ -680,18 +685,12 @@ public class InitialHandler extends PacketHandler implements PendingConnection
@Override
public void done(PostLoginEvent result, Throwable error)
{
// #3612: Don't progress further if disconnected during event
if ( ch.isClosing() )
{
return;
}
userCon.connect( result.getTarget(), null, true, ServerConnectEvent.Reason.JOIN_PROXY );
}
};
// fire post-login event
bungee.getPluginManager().callEvent( new PostLoginEvent( userCon, initialServer, complete ) );
bungee.getPluginManager().callEvent( new PostLoginEvent( userCon, initialServer, eventLoopCallback( complete ) ) );
}
@Override
@ -704,6 +703,9 @@ public class InitialHandler extends PacketHandler implements PendingConnection
}
Preconditions.checkState( future != null, "Unexpected custom LoginPayloadResponse" );
future.complete( response.getData() );
// we should never pass this to the backend
throw CancelSendSignal.INSTANCE;
}
@Override
@ -781,6 +783,59 @@ public class InitialHandler extends PacketHandler implements PendingConnection
return ( name != null ) ? name : ( loginRequest == null ) ? null : loginRequest.getData();
}
private void setName(String name)
{
this.name = name;
if ( loginRequest != null )
{
loginRequest.setData( name ); // name transmitted to Spigot server
}
updateOfflineId();
}
private UUID generateDuplicatedId(UUID base)
{
// UUID version: offline = 3 ; Java online mode = 4 ; Floodgate xUID = 0 (and must be kept 0)
// UUID variant: offline = 0xx ; Java online mode = 10x ; Floodgate xUID = xxx
if ( base.version() == 0 )
{
/*
* Floodgates xUID converted to UUID are not supported
* because it requires the 64 MSBs to be 0 (or Floodgate API would not
* recognize a Bedrock account) and we cannot modify the 64 LSBs
* without risking a collision with the xUID of another Bedrock account
*/
return base;
}
long MSB = base.getMostSignificantBits();
long LSB = base.getLeastSignificantBits();
MSB &= 0xFFFFFFFF_FFFF_70FFL; // reset bits we need
MSB |= 0x00000000_0000_8000L; // set version to + 8 the current version
for ( int i = 1; i <= 9; i++ )
{
long newMSB = MSB | i << 8;
UUID newUUID = new UUID( newMSB, LSB );
if ( bungee.getPlayer( newUUID ) != null )
{
continue;
}
return newUUID;
}
return base; // there are too many duplicated connections for this player
}
private static int getDuplicationIndex(UUID duplicatedId)
{
return (int) ( duplicatedId.getMostSignificantBits() >> 8 ) & 0xF;
}
private void updateOfflineId()
{
offlineId = UUID.nameUUIDFromBytes( ( "OfflinePlayer:" + getName() ).getBytes( StandardCharsets.UTF_8 ) );
}
@Override
public int getVersion()
{
@ -917,4 +972,32 @@ public class InitialHandler extends PacketHandler implements PendingConnection
unsafe.sendPacket( new LoginPayloadRequest( id, channel, data ) );
return future;
}
// this method is used for event execution
// if this connection is disconnected during an event-call, the original callback is not called
// if the event was executed async, we execute the callback on the eventloop again
// otherwise netty will schedule any pipeline related call by itself, this decreases performance
private <T> Callback<T> eventLoopCallback(Callback<T> callback)
{
return (result, error) ->
{
EventLoop eventLoop = ch.getHandle().eventLoop();
if ( eventLoop.inEventLoop() )
{
if ( !ch.isClosing() )
{
callback.done( result, error );
}
return;
}
eventLoop.execute( () ->
{
if ( !ch.isClosing() )
{
callback.done( result, error );
}
} );
};
}
}

View File

@ -1,5 +1,6 @@
package net.md_5.bungee.connection;
import com.google.gson.Gson;
import lombok.AllArgsConstructor;
import lombok.Data;
import net.md_5.bungee.protocol.Property;
@ -9,6 +10,8 @@ import net.md_5.bungee.protocol.Property;
public class LoginResult
{
public static final Gson GSON = new Gson();
//
private String id;
private String name;
private Property[] properties;

Some files were not shown because too many files have changed in this diff Show More