1
0
Fork 0

Compare commits

...

34 Commits

Author SHA1 Message Date
Marc Baloup 6bb83c4e61 new event TabCompleteRequestEvent and deprecate TabCompleteEvent 2024-02-17 13:40:29 +01:00
Marc Baloup e28802d3ff Add CommandsDeclareEvent to declare commands with brigadier API 2024-02-17 13:40:29 +01:00
Marc Baloup 754798b372 Server branding now includes the backend server name 2024-02-17 13:40:29 +01:00
Marc Baloup 0bc21968d9 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.
2024-02-17 13:40:29 +01:00
Marc Baloup e41691bd57 Change projet configuration and POM for Pandacube 2024-02-17 13:40:29 +01:00
Marc Baloup c237bd3895 Remove modules and startup delay
We don’t need them for Pandacube
2024-02-17 13:40:26 +01:00
dependabot[bot] 3e9a7e45c4
#3616: Bump io.netty:netty-bom from 4.1.106.Final to 4.1.107.Final
Bumps [io.netty:netty-bom](https://github.com/netty/netty) from 4.1.106.Final to 4.1.107.Final.
- [Commits](https://github.com/netty/netty/compare/netty-4.1.106.Final...netty-4.1.107.Final)

---
updated-dependencies:
- dependency-name: io.netty:netty-bom
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-02-17 13:41:16 +11:00
dependabot[bot] f6c5332c1a
#3613: Bump org.junit.jupiter:junit-jupiter from 5.10.1 to 5.10.2
Bumps [org.junit.jupiter:junit-jupiter](https://github.com/junit-team/junit5) from 5.10.1 to 5.10.2.
- [Release notes](https://github.com/junit-team/junit5/releases)
- [Commits](https://github.com/junit-team/junit5/compare/r5.10.1...r5.10.2)

---
updated-dependencies:
- dependency-name: org.junit.jupiter:junit-jupiter
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-02-17 13:41:00 +11:00
md_5 d0fa62d424
Minecraft 24w06a support 2024-02-10 12:26:38 +11:00
md_5 464ed0184c
Improve cookie support during login 2024-02-10 12:26:26 +11:00
md_5 eda268b481
Fix 24w05b spectate packet ID 2024-02-06 07:03:49 +11:00
md_5 3e1007527c
#3612: Error when disconnecting player on PostLoginEvent 2024-02-04 11:47:30 +11:00
md_5 b52b14696c
Add PendingConnection#isTransferred API method 2024-02-04 11:35:20 +11:00
md_5 94d5b0d03c
Minecraft 24w05b support 2024-02-03 15:34:08 +11:00
Diogo Correia c3f228f626
#3610, 3611: inverted isEmpty method on ComponentStyle 2024-02-02 11:16:46 +11:00
Outfluencer 02c5c1ee76
#3602: Minecraft 24w04a support 2024-01-30 07:25:22 +11:00
md_5 c69acf728c
Add JetBrains java-annotations 2024-01-29 20:29:58 +11:00
md_5 a1cd694363
Bump version to 1.20-R0.3-SNAPSHOT 2024-01-20 08:41:57 +11:00
md_5 3e2bc8e2d7
Release 1.20-R0.2 2024-01-20 08:37:37 +11:00
dependabot[bot] ad7163d2d6
#3600: Bump io.netty:netty-bom from 4.1.104.Final to 4.1.106.Final
Bumps [io.netty:netty-bom](https://github.com/netty/netty) from 4.1.104.Final to 4.1.106.Final.
- [Commits](https://github.com/netty/netty/compare/netty-4.1.104.Final...netty-4.1.106.Final)

---
updated-dependencies:
- dependency-name: io.netty:netty-bom
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-01-20 08:34:51 +11:00
Valentine 19918c694f
#3594: Fix missing ComponentStyleSerializer 2024-01-09 16:09:55 +11:00
md_5 21c8f2815a
Decode ComponentStyle over network as appropriate 2024-01-06 16:35:33 +11:00
Parker Hawke 737d545fb6
#3569: Separate component styling into a ComponentStyle class 2024-01-06 16:35:30 +11:00
dependabot[bot] b23a51825e
#3588: Bump org.apache.maven.plugins:maven-compiler-plugin
Bumps [org.apache.maven.plugins:maven-compiler-plugin](https://github.com/apache/maven-compiler-plugin) from 3.11.0 to 3.12.1.
- [Release notes](https://github.com/apache/maven-compiler-plugin/releases)
- [Commits](https://github.com/apache/maven-compiler-plugin/compare/maven-compiler-plugin-3.11.0...maven-compiler-plugin-3.12.1)

---
updated-dependencies:
- dependency-name: org.apache.maven.plugins:maven-compiler-plugin
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-12-26 08:47:34 +11:00
md_5 708c5b6254
#3585: Fix mistake converting certain NBT to JSON in previous commit 2023-12-22 22:06:23 +11:00
md_5 f5af11193c
#3584: Handle conversion of mixed NBT lists to json 2023-12-22 18:01:30 +11:00
md_5 b711e4033f
#3578: bungeecord-chat does not support array format UUIDs 2023-12-19 19:54:03 +11:00
Outfluencer 3deaaadc3a
#3574: "VarInt too big" should be an OverflowPacketException 2023-12-16 11:29:03 +11:00
dependabot[bot] 51b9a6b0b8
#3577: Bump io.netty:netty-bom from 4.1.101.Final to 4.1.104.Final
Bumps [io.netty:netty-bom](https://github.com/netty/netty) from 4.1.101.Final to 4.1.104.Final.
- [Commits](https://github.com/netty/netty/compare/netty-4.1.101.Final...netty-4.1.104.Final)

---
updated-dependencies:
- dependency-name: io.netty:netty-bom
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-12-16 11:28:36 +11:00
dependabot[bot] 1a807731a5
#3567: Bump org.apache.maven.plugins:maven-javadoc-plugin from 3.6.2 to 3.6.3
Bumps [org.apache.maven.plugins:maven-javadoc-plugin](https://github.com/apache/maven-javadoc-plugin) from 3.6.2 to 3.6.3.
- [Release notes](https://github.com/apache/maven-javadoc-plugin/releases)
- [Commits](https://github.com/apache/maven-javadoc-plugin/compare/maven-javadoc-plugin-3.6.2...maven-javadoc-plugin-3.6.3)

---
updated-dependencies:
- dependency-name: org.apache.maven.plugins:maven-javadoc-plugin
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-12-10 11:00:38 +11:00
dependabot[bot] 772ad9951f
#3566: Bump actions/setup-java from 3 to 4
Bumps [actions/setup-java](https://github.com/actions/setup-java) from 3 to 4.
- [Release notes](https://github.com/actions/setup-java/releases)
- [Commits](https://github.com/actions/setup-java/compare/v3...v4)

---
updated-dependencies:
- dependency-name: actions/setup-java
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-12-10 11:00:24 +11:00
dependabot[bot] 2431c40a5c
#3562: Bump io.netty:netty-bom from 4.1.100.Final to 4.1.101.Final
Bumps [io.netty:netty-bom](https://github.com/netty/netty) from 4.1.100.Final to 4.1.101.Final.
- [Commits](https://github.com/netty/netty/compare/netty-4.1.100.Final...netty-4.1.101.Final)

---
updated-dependencies:
- dependency-name: io.netty:netty-bom
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-12-10 11:00:05 +11:00
dependabot[bot] 8144ae8d7b
#3555: Bump com.mysql:mysql-connector-j from 8.1.0 to 8.2.0
Bumps [com.mysql:mysql-connector-j](https://github.com/mysql/mysql-connector-j) from 8.1.0 to 8.2.0.
- [Changelog](https://github.com/mysql/mysql-connector-j/blob/release/8.x/CHANGES)
- [Commits](https://github.com/mysql/mysql-connector-j/compare/8.1.0...8.2.0)

---
updated-dependencies:
- dependency-name: com.mysql:mysql-connector-j
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-12-10 10:59:52 +11:00
md_5 0757c39a6f
Attempt upgrade of resolver libraries 2023-12-10 10:56:39 +11:00
96 changed files with 2379 additions and 1874 deletions

View File

@ -13,10 +13,9 @@ updates:
- dependency-name: "com.puppycrawl.tools:checkstyle"
# Newer versions have issues, see #1909 and #2050
- dependency-name: "jline:jline"
# Later versions of these Maven dependencies are incompatible and require careful management - see SPIGOT-7400
# Needs to be synchronised with maven-resolver-provider dependencies
- dependency-name: "org.apache.maven.resolver:maven-resolver-connector-basic"
- dependency-name: "org.apache.maven.resolver:maven-resolver-transport-http"
- dependency-name: "org.apache.maven:maven-resolver-provider"
# Used with maven-resolver dependencies; 2.0 update breaks other providers
- dependency-name: "org.slf4j:slf4j-api"
update-types: ["version-update:semver-major"]

View File

@ -15,7 +15,7 @@ jobs:
steps:
- uses: actions/checkout@v4
- uses: actions/setup-java@v3
- uses: actions/setup-java@v4
with:
distribution: zulu
java-version: ${{ matrix.java }}

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.20-R0.2-SNAPSHOT</version>
<version>1.20-R0.3-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<groupId>net.md-5</groupId>
<artifactId>bungeecord-api</artifactId>
<version>1.20-R0.2-SNAPSHOT</version>
<version>1.20-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>
@ -51,21 +50,21 @@
<dependency>
<groupId>org.apache.maven</groupId>
<artifactId>maven-resolver-provider</artifactId>
<version>3.8.5</version>
<version>3.9.6</version>
<!-- not part of the API proper -->
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.maven.resolver</groupId>
<artifactId>maven-resolver-connector-basic</artifactId>
<version>1.7.3</version>
<version>1.9.18</version>
<!-- not part of the API proper -->
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.maven.resolver</groupId>
<artifactId>maven-resolver-transport-http</artifactId>
<version>1.7.3</version>
<version>1.9.18</version>
<!-- not part of the API proper -->
<scope>provided</scope>
</dependency>

View File

@ -2,7 +2,9 @@ package net.md_5.bungee.api.connection;
import java.net.InetSocketAddress;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import net.md_5.bungee.api.config.ListenerInfo;
import org.jetbrains.annotations.ApiStatus;
/**
* Represents a user attempting to log into the proxy.
@ -89,4 +91,26 @@ public interface PendingConnection extends Connection
* @return Whether the client is using a legacy client.
*/
boolean isLegacy();
/**
* Gets if this connection has been transferred from another server.
*
* @return true if the connection has been transferred
*/
@ApiStatus.Experimental
boolean isTransferred();
/**
* Retrieves a cookie from this pending connection.
*
* @param cookie the resource location of the cookie, for example
* "bungeecord:my_cookie"
* @return a {@link CompletableFuture} that will be completed when the
* Cookie response is received. If the cookie is not set in the client, the
* {@link CompletableFuture} will complete with a null value
* @throws IllegalStateException if the player's version is not at least
* 1.20.5
*/
@ApiStatus.Experimental
CompletableFuture<byte[]> retrieveCookie(String cookie);
}

View File

@ -3,6 +3,7 @@ package net.md_5.bungee.api.connection;
import java.util.Locale;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import net.md_5.bungee.api.Callback;
import net.md_5.bungee.api.ChatMessageType;
import net.md_5.bungee.api.CommandSender;
@ -13,6 +14,7 @@ import net.md_5.bungee.api.chat.BaseComponent;
import net.md_5.bungee.api.config.ServerInfo;
import net.md_5.bungee.api.event.ServerConnectEvent;
import net.md_5.bungee.api.score.Scoreboard;
import org.jetbrains.annotations.ApiStatus;
/**
* Represents a player whose connection is being connected to somewhere else,
@ -339,4 +341,45 @@ public interface ProxiedPlayer extends Connection, CommandSender
*/
@Deprecated
Scoreboard getScoreboard();
/**
* Retrieves a cookie from this player.
*
* @param cookie the resource location of the cookie, for example
* "bungeecord:my_cookie"
* @return a {@link CompletableFuture} that will be completed when the
* Cookie response is received. If the cookie is not set in the client, the
* {@link CompletableFuture} will complete with a null value
* @throws IllegalStateException if the player's version is not at least
* 1.20.5
*/
@ApiStatus.Experimental
CompletableFuture<byte[]> retrieveCookie(String cookie);
/**
* Stores a cookie in this player's client.
*
* @param cookie the resource location of the cookie, for example
* "bungeecord:my_cookie"
* @param data the data to store in the cookie
* @throws IllegalStateException if the player's version is not at least
* 1.20.5
*/
@ApiStatus.Experimental
void storeCookie(String cookie, byte[] data);
/**
* Requests this player to connect to a different server specified by host
* and port.
*
* This is a client-side transfer - host and port should not specify a
* BungeeCord backend server.
*
* @param host the host of the server to transfer to
* @param port the port of the server to transfer to
* @throws IllegalStateException if the players version is not at least
* 1.20.5
*/
@ApiStatus.Experimental
void transfer(String host, int port);
}

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

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

@ -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.20-R0.2-SNAPSHOT</version>
<version>1.20-R0.3-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<groupId>net.md-5</groupId>
<artifactId>bungeecord-bootstrap</artifactId>
<version>1.20-R0.2-SNAPSHOT</version>
<version>1.20-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>

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.20-R0.2-SNAPSHOT</version>
<version>1.20-R0.3-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<groupId>net.md-5</groupId>
<artifactId>bungeecord-chat</artifactId>
<version>1.20-R0.2-SNAPSHOT</version>
<version>1.20-R0.3-SNAPSHOT</version>
<packaging>jar</packaging>
<name>BungeeCord-Chat</name>

View File

@ -20,38 +20,10 @@ public abstract class BaseComponent
BaseComponent parent;
/**
* The color of this component and any child components (unless overridden)
* The component's style.
*/
private ChatColor color;
/**
* The font of this component and any child components (unless overridden)
*/
private String font;
/**
* Whether this component and any child components (unless overridden) is
* bold
*/
private Boolean bold;
/**
* Whether this component and any child components (unless overridden) is
* italic
*/
private Boolean italic;
/**
* Whether this component and any child components (unless overridden) is
* underlined
*/
private Boolean underlined;
/**
* Whether this component and any child components (unless overridden) is
* strikethrough
*/
private Boolean strikethrough;
/**
* Whether this component and any child components (unless overridden) is
* obfuscated
*/
private Boolean obfuscated;
@Getter
private ComponentStyle style = new ComponentStyle();
/**
* The text to insert into the chat when this component (and child
* components) are clicked while pressing the shift key
@ -153,31 +125,31 @@ public abstract class BaseComponent
}
if ( retention == FormatRetention.FORMATTING || retention == FormatRetention.ALL )
{
if ( replace || color == null )
if ( replace || !style.hasColor() )
{
setColor( component.getColorRaw() );
}
if ( replace || font == null )
if ( replace || !style.hasFont() )
{
setFont( component.getFontRaw() );
}
if ( replace || bold == null )
if ( replace || style.isBoldRaw() == null )
{
setBold( component.isBoldRaw() );
}
if ( replace || italic == null )
if ( replace || style.isItalicRaw() == null )
{
setItalic( component.isItalicRaw() );
}
if ( replace || underlined == null )
if ( replace || style.isUnderlinedRaw() == null )
{
setUnderlined( component.isUnderlinedRaw() );
}
if ( replace || strikethrough == null )
if ( replace || style.isStrikethroughRaw() == null )
{
setStrikethrough( component.isStrikethroughRaw() );
}
if ( replace || obfuscated == null )
if ( replace || style.isObfuscatedRaw() == null )
{
setObfuscated( component.isObfuscatedRaw() );
}
@ -266,6 +238,29 @@ public abstract class BaseComponent
return builder.toString();
}
/**
* Set the {@link ComponentStyle} for this component.
* <p>
* Unlike {@link #applyStyle(ComponentStyle)}, this method will overwrite
* all style values on this component.
*
* @param style the style to set, or null to set all style values to default
*/
public void setStyle(ComponentStyle style)
{
this.style = ( style != null ) ? style.clone() : new ComponentStyle();
}
/**
* Set this component's color.
*
* @param color the component color, or null to use the default
*/
public void setColor(ChatColor color)
{
this.style.setColor( color );
}
/**
* Returns the color of this component. This uses the parent's color if this
* component doesn't have one. {@link net.md_5.bungee.api.ChatColor#WHITE}
@ -275,7 +270,7 @@ public abstract class BaseComponent
*/
public ChatColor getColor()
{
if ( color == null )
if ( !style.hasColor() )
{
if ( parent == null )
{
@ -283,7 +278,7 @@ public abstract class BaseComponent
}
return parent.getColor();
}
return color;
return style.getColor();
}
/**
@ -294,7 +289,17 @@ public abstract class BaseComponent
*/
public ChatColor getColorRaw()
{
return color;
return style.getColor();
}
/**
* Set this component's font.
*
* @param font the font to set, or null to use the default
*/
public void setFont(String font)
{
this.style.setFont( font );
}
/**
@ -305,7 +310,7 @@ public abstract class BaseComponent
*/
public String getFont()
{
if ( font == null )
if ( !style.hasFont() )
{
if ( parent == null )
{
@ -313,7 +318,7 @@ public abstract class BaseComponent
}
return parent.getFont();
}
return font;
return style.getFont();
}
/**
@ -324,7 +329,17 @@ public abstract class BaseComponent
*/
public String getFontRaw()
{
return font;
return style.getFont();
}
/**
* Set whether or not this component is bold.
*
* @param bold the new bold state, or null to use the default
*/
public void setBold(Boolean bold)
{
this.style.setBold( bold );
}
/**
@ -336,11 +351,11 @@ public abstract class BaseComponent
*/
public boolean isBold()
{
if ( bold == null )
if ( style.isBoldRaw() == null )
{
return parent != null && parent.isBold();
}
return bold;
return style.isBold();
}
/**
@ -351,7 +366,17 @@ public abstract class BaseComponent
*/
public Boolean isBoldRaw()
{
return bold;
return style.isBoldRaw();
}
/**
* Set whether or not this component is italic.
*
* @param italic the new italic state, or null to use the default
*/
public void setItalic(Boolean italic)
{
this.style.setItalic( italic );
}
/**
@ -363,11 +388,11 @@ public abstract class BaseComponent
*/
public boolean isItalic()
{
if ( italic == null )
if ( style.isItalicRaw() == null )
{
return parent != null && parent.isItalic();
}
return italic;
return style.isItalic();
}
/**
@ -378,7 +403,17 @@ public abstract class BaseComponent
*/
public Boolean isItalicRaw()
{
return italic;
return style.isItalicRaw();
}
/**
* Set whether or not this component is underlined.
*
* @param underlined the new underlined state, or null to use the default
*/
public void setUnderlined(Boolean underlined)
{
this.style.setUnderlined( underlined );
}
/**
@ -390,11 +425,11 @@ public abstract class BaseComponent
*/
public boolean isUnderlined()
{
if ( underlined == null )
if ( style.isUnderlinedRaw() == null )
{
return parent != null && parent.isUnderlined();
}
return underlined;
return style.isUnderlined();
}
/**
@ -405,7 +440,18 @@ public abstract class BaseComponent
*/
public Boolean isUnderlinedRaw()
{
return underlined;
return style.isUnderlinedRaw();
}
/**
* Set whether or not this component is strikethrough.
*
* @param strikethrough the new strikethrough state, or null to use the
* default
*/
public void setStrikethrough(Boolean strikethrough)
{
this.style.setStrikethrough( strikethrough );
}
/**
@ -417,11 +463,11 @@ public abstract class BaseComponent
*/
public boolean isStrikethrough()
{
if ( strikethrough == null )
if ( style.isStrikethroughRaw() == null )
{
return parent != null && parent.isStrikethrough();
}
return strikethrough;
return style.isStrikethrough();
}
/**
@ -432,7 +478,17 @@ public abstract class BaseComponent
*/
public Boolean isStrikethroughRaw()
{
return strikethrough;
return style.isStrikethroughRaw();
}
/**
* Set whether or not this component is obfuscated.
*
* @param obfuscated the new obfuscated state, or null to use the default
*/
public void setObfuscated(Boolean obfuscated)
{
this.style.setObfuscated( obfuscated );
}
/**
@ -444,11 +500,11 @@ public abstract class BaseComponent
*/
public boolean isObfuscated()
{
if ( obfuscated == null )
if ( style.isObfuscatedRaw() == null )
{
return parent != null && parent.isObfuscated();
}
return obfuscated;
return style.isObfuscated();
}
/**
@ -459,7 +515,48 @@ public abstract class BaseComponent
*/
public Boolean isObfuscatedRaw()
{
return obfuscated;
return style.isObfuscatedRaw();
}
/**
* Apply the style from the given {@link ComponentStyle} to this component.
* <p>
* Any style values that have been explicitly set in the style will be
* applied to this component. If a value is not set in the style, it will
* not override the style set in this component.
*
* @param style the style to apply
*/
public void applyStyle(ComponentStyle style)
{
if ( style.hasColor() )
{
setColor( style.getColor() );
}
if ( style.hasFont() )
{
setFont( style.getFont() );
}
if ( style.isBoldRaw() != null )
{
setBold( style.isBoldRaw() );
}
if ( style.isItalicRaw() != null )
{
setItalic( style.isItalicRaw() );
}
if ( style.isUnderlinedRaw() != null )
{
setUnderlined( style.isUnderlinedRaw() );
}
if ( style.isStrikethroughRaw() != null )
{
setStrikethrough( style.isStrikethroughRaw() );
}
if ( style.isObfuscatedRaw() != null )
{
setObfuscated( style.isObfuscatedRaw() );
}
}
public void setExtra(List<BaseComponent> components)
@ -498,6 +595,16 @@ public abstract class BaseComponent
extra.add( component );
}
/**
* Returns whether the component has any styling applied to it.
*
* @return Whether any styling is applied
*/
public boolean hasStyle()
{
return !style.isEmpty();
}
/**
* Returns whether the component has any formatting or events applied to it
*
@ -505,10 +612,8 @@ public abstract class BaseComponent
*/
public boolean hasFormatting()
{
return color != null || font != null || bold != null
|| italic != null || underlined != null
|| strikethrough != null || obfuscated != null
|| insertion != null || hoverEvent != null || clickEvent != null;
return hasStyle() || insertion != null
|| hoverEvent != null || clickEvent != null;
}
/**

View File

@ -423,6 +423,18 @@ public final class ComponentBuilder
return this;
}
/**
* Applies the provided {@link ComponentStyle} to the current part.
*
* @param style the style to apply
* @return this ComponentBuilder for chaining
*/
public ComponentBuilder style(ComponentStyle style)
{
getCurrentComponent().applyStyle( style );
return this;
}
/**
* Sets the insertion text for the current part.
*

View File

@ -0,0 +1,234 @@
package net.md_5.bungee.api.chat;
import lombok.AllArgsConstructor;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import lombok.Setter;
import net.md_5.bungee.api.ChatColor;
/**
* Represents a style that may be applied to a {@link BaseComponent}.
*/
@Setter
@AllArgsConstructor
@NoArgsConstructor
@EqualsAndHashCode
public final class ComponentStyle implements Cloneable
{
/**
* The color of this style.
*/
private ChatColor color;
/**
* The font of this style.
*/
private String font;
/**
* Whether this style is bold.
*/
private Boolean bold;
/**
* Whether this style is italic.
*/
private Boolean italic;
/**
* Whether this style is underlined.
*/
private Boolean underlined;
/**
* Whether this style is strikethrough.
*/
private Boolean strikethrough;
/**
* Whether this style is obfuscated.
*/
private Boolean obfuscated;
/**
* Returns the color of this style. May return null.
*
* @return the color of this style, or null if default color
*/
public ChatColor getColor()
{
return color;
}
/**
* Returns whether or not this style has a color set.
*
* @return whether a color is set
*/
public boolean hasColor()
{
return ( color != null );
}
/**
* Returns the font of this style. May return null.
*
* @return the font of this style, or null if default font
*/
public String getFont()
{
return font;
}
/**
* Returns whether or not this style has a font set.
*
* @return whether a font is set
*/
public boolean hasFont()
{
return ( font != null );
}
/**
* Returns whether this style is bold.
*
* @return whether the style is bold
*/
public boolean isBold()
{
return ( bold != null ) && bold.booleanValue();
}
/**
* Returns whether this style is bold. May return null.
*
* @return whether the style is bold, or null if not set
*/
public Boolean isBoldRaw()
{
return bold;
}
/**
* Returns whether this style is italic. May return null.
*
* @return whether the style is italic
*/
public boolean isItalic()
{
return ( italic != null ) && italic.booleanValue();
}
/**
* Returns whether this style is italic. May return null.
*
* @return whether the style is italic, or null if not set
*/
public Boolean isItalicRaw()
{
return italic;
}
/**
* Returns whether this style is underlined.
*
* @return whether the style is underlined
*/
public boolean isUnderlined()
{
return ( underlined != null ) && underlined.booleanValue();
}
/**
* Returns whether this style is underlined. May return null.
*
* @return whether the style is underlined, or null if not set
*/
public Boolean isUnderlinedRaw()
{
return underlined;
}
/**
* Returns whether this style is strikethrough
*
* @return whether the style is strikethrough
*/
public boolean isStrikethrough()
{
return ( strikethrough != null ) && strikethrough.booleanValue();
}
/**
* Returns whether this style is strikethrough. May return null.
*
* @return whether the style is strikethrough, or null if not set
*/
public Boolean isStrikethroughRaw()
{
return strikethrough;
}
/**
* Returns whether this style is obfuscated.
*
* @return whether the style is obfuscated
*/
public boolean isObfuscated()
{
return ( obfuscated != null ) && obfuscated.booleanValue();
}
/**
* Returns whether this style is obfuscated. May return null.
*
* @return whether the style is obfuscated, or null if not set
*/
public Boolean isObfuscatedRaw()
{
return obfuscated;
}
/**
* Returns whether this style has no formatting explicitly set.
*
* @return true if no value is set, false if at least one is set
*/
public boolean isEmpty()
{
return color == null && font == null && bold == null
&& italic == null && underlined == null
&& strikethrough == null && obfuscated == null;
}
@Override
public ComponentStyle clone()
{
return new ComponentStyle( color, font, bold, italic, underlined, strikethrough, obfuscated );
}
/**
* Get a new {@link ComponentStyleBuilder}.
*
* @return the builder
*/
public static ComponentStyleBuilder builder()
{
return new ComponentStyleBuilder();
}
/**
* Get a new {@link ComponentStyleBuilder} with values initialized to the
* style values of the supplied {@link ComponentStyle}.
*
* @param other the component style whose values to copy into the builder
* @return the builder
*/
public static ComponentStyleBuilder builder(ComponentStyle other)
{
return new ComponentStyleBuilder()
.color( other.color )
.font( other.font )
.bold( other.bold )
.italic( other.italic )
.underlined( other.underlined )
.strikethrough( other.strikethrough )
.obfuscated( other.obfuscated );
}
}

View File

@ -0,0 +1,126 @@
package net.md_5.bungee.api.chat;
import net.md_5.bungee.api.ChatColor;
/**
* <p>
* ComponentStyleBuilder simplifies creating component styles by allowing the
* use of a chainable builder.
* </p>
* <pre>
* ComponentStyle style = ComponentStyle.builder()
* .color(ChatColor.RED)
* .font("custom:font")
* .bold(true).italic(true).create();
*
* BaseComponent component = new ComponentBuilder("Hello world").style(style).create();
* // Or it can be used directly on a component
* TextComponent text = new TextComponent("Hello world");
* text.applyStyle(style);
* </pre>
*
* @see ComponentStyle#builder()
* @see ComponentStyle#builder(ComponentStyle)
*/
public final class ComponentStyleBuilder
{
private ChatColor color;
private String font;
private Boolean bold, italic, underlined, strikethrough, obfuscated;
/**
* Set the style color.
*
* @param color the color to set, or null to use the default
* @return this ComponentStyleBuilder for chaining
*/
public ComponentStyleBuilder color(ChatColor color)
{
this.color = color;
return this;
}
/**
* Set the style font.
*
* @param font the font key to set, or null to use the default
* @return this ComponentStyleBuilder for chaining
*/
public ComponentStyleBuilder font(String font)
{
this.font = font;
return this;
}
/**
* Set the style's bold property.
*
* @param bold the bold value to set, or null to use the default
* @return this ComponentStyleBuilder for chaining
*/
public ComponentStyleBuilder bold(Boolean bold)
{
this.bold = bold;
return this;
}
/**
* Set the style's italic property.
*
* @param italic the italic value to set, or null to use the default
* @return this ComponentStyleBuilder for chaining
*/
public ComponentStyleBuilder italic(Boolean italic)
{
this.italic = italic;
return this;
}
/**
* Set the style's underlined property.
*
* @param underlined the underlined value to set, or null to use the default
* @return this ComponentStyleBuilder for chaining
*/
public ComponentStyleBuilder underlined(Boolean underlined)
{
this.underlined = underlined;
return this;
}
/**
* Set the style's strikethrough property.
*
* @param strikethrough the strikethrough value to set, or null to use the
* default
* @return this ComponentStyleBuilder for chaining
*/
public ComponentStyleBuilder strikethrough(Boolean strikethrough)
{
this.strikethrough = strikethrough;
return this;
}
/**
* Set the style's obfuscated property.
*
* @param obfuscated the obfuscated value to set, or null to use the default
* @return this ComponentStyleBuilder for chaining
*/
public ComponentStyleBuilder obfuscated(Boolean obfuscated)
{
this.obfuscated = obfuscated;
return this;
}
/**
* Build the {@link ComponentStyle} using the values set in this builder.
*
* @return the created ComponentStyle
*/
public ComponentStyle build()
{
return new ComponentStyle( color, font, bold, italic, underlined, strikethrough, obfuscated );
}
}

View File

@ -8,6 +8,7 @@ import com.google.gson.JsonParseException;
import com.google.gson.JsonSerializationContext;
import com.google.gson.JsonSerializer;
import java.lang.reflect.Type;
import java.util.UUID;
import net.md_5.bungee.api.chat.BaseComponent;
public class EntitySerializer implements JsonSerializer<Entity>, JsonDeserializer<Entity>
@ -18,9 +19,19 @@ public class EntitySerializer implements JsonSerializer<Entity>, JsonDeserialize
{
JsonObject value = element.getAsJsonObject();
String idString;
JsonElement id = value.get( "id" );
if ( id.isJsonArray() )
{
idString = parseUUID( context.deserialize( id, int[].class ) ).toString();
} else
{
idString = id.getAsString();
}
return new Entity(
( value.has( "type" ) ) ? value.get( "type" ).getAsString() : null,
value.get( "id" ).getAsString(),
idString,
( value.has( "name" ) ) ? context.deserialize( value.get( "name" ), BaseComponent.class ) : null
);
}
@ -37,4 +48,9 @@ public class EntitySerializer implements JsonSerializer<Entity>, JsonDeserialize
}
return object;
}
private static UUID parseUUID(int[] array)
{
return new UUID( (long) array[0] << 32 | (long) array[1] & 0XFFFFFFFFL, (long) array[2] << 32 | (long) array[3] & 0XFFFFFFFFL );
}
}

View File

@ -4,73 +4,25 @@ import com.google.common.base.Preconditions;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;
import com.google.gson.JsonPrimitive;
import com.google.gson.JsonSerializationContext;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.IdentityHashMap;
import java.util.Locale;
import net.md_5.bungee.api.ChatColor;
import net.md_5.bungee.api.chat.BaseComponent;
import net.md_5.bungee.api.chat.ClickEvent;
import net.md_5.bungee.api.chat.ComponentStyle;
import net.md_5.bungee.api.chat.HoverEvent;
import net.md_5.bungee.api.chat.hover.content.Content;
public class BaseComponentSerializer
{
private static boolean getAsBoolean(JsonElement el)
{
if ( el.isJsonPrimitive() )
{
JsonPrimitive primitive = (JsonPrimitive) el;
if ( primitive.isBoolean() )
{
return primitive.getAsBoolean();
}
if ( primitive.isNumber() )
{
Number number = primitive.getAsNumber();
if ( number instanceof Byte )
{
return number.byteValue() != 0;
}
}
}
return false;
}
protected void deserialize(JsonObject object, BaseComponent component, JsonDeserializationContext context)
{
if ( object.has( "bold" ) )
{
component.setBold( getAsBoolean( object.get( "bold" ) ) );
}
if ( object.has( "italic" ) )
{
component.setItalic( getAsBoolean( object.get( "italic" ) ) );
}
if ( object.has( "underlined" ) )
{
component.setUnderlined( getAsBoolean( object.get( "underlined" ) ) );
}
if ( object.has( "strikethrough" ) )
{
component.setStrikethrough( getAsBoolean( object.get( "strikethrough" ) ) );
}
if ( object.has( "obfuscated" ) )
{
component.setObfuscated( getAsBoolean( object.get( "obfuscated" ) ) );
}
if ( object.has( "color" ) )
{
component.setColor( ChatColor.of( object.get( "color" ).getAsString() ) );
}
component.applyStyle( context.deserialize( object, ComponentStyle.class ) );
if ( object.has( "insertion" ) )
{
component.setInsertion( object.get( "insertion" ).getAsString() );
@ -90,59 +42,48 @@ public class BaseComponentSerializer
HoverEvent hoverEvent = null;
HoverEvent.Action action = HoverEvent.Action.valueOf( event.get( "action" ).getAsString().toUpperCase( Locale.ROOT ) );
for ( String type : Arrays.asList( "value", "contents" ) )
if ( event.has( "value" ) )
{
if ( !event.has( type ) )
{
continue;
}
JsonElement contents = event.get( type );
try
{
JsonElement contents = event.get( "value" );
// Plugins previously had support to pass BaseComponent[] into any action.
// If the GSON is possible to be parsed as BaseComponent, attempt to parse as so.
BaseComponent[] components;
if ( contents.isJsonArray() )
{
components = context.deserialize( contents, BaseComponent[].class );
} else
{
components = new BaseComponent[]
{
context.deserialize( contents, BaseComponent.class )
};
}
hoverEvent = new HoverEvent( action, components );
} catch ( JsonParseException ex )
// Plugins previously had support to pass BaseComponent[] into any action.
// If the GSON is possible to be parsed as BaseComponent, attempt to parse as so.
BaseComponent[] components;
if ( contents.isJsonArray() )
{
Content[] list;
if ( contents.isJsonArray() )
components = context.deserialize( contents, BaseComponent[].class );
} else
{
components = new BaseComponent[]
{
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 ) ) );
context.deserialize( contents, BaseComponent.class )
};
}
hoverEvent = new HoverEvent( action, components );
} else if ( event.has( "contents" ) )
{
JsonElement contents = event.get( "contents" );
// stop the loop as soon as either one is found
break;
Content[] list;
if ( contents.isJsonArray() )
{
list = context.deserialize( contents, HoverEvent.getClass( action, true ) );
} else
{
list = new Content[]
{
context.deserialize( contents, HoverEvent.getClass( action, false ) )
};
}
hoverEvent = new HoverEvent( action, new ArrayList<>( Arrays.asList( list ) ) );
}
if ( hoverEvent != null )
{
component.setHoverEvent( hoverEvent );
}
}
if ( object.has( "font" ) )
{
component.setFont( object.get( "font" ).getAsString() );
}
if ( object.has( "extra" ) )
{
component.setExtra( Arrays.asList( context.<BaseComponent[]>deserialize( object.get( "extra" ), BaseComponent[].class ) ) );
@ -161,30 +102,9 @@ public class BaseComponentSerializer
{
Preconditions.checkArgument( !ComponentSerializer.serializedComponents.get().contains( component ), "Component loop" );
ComponentSerializer.serializedComponents.get().add( component );
if ( component.isBoldRaw() != null )
{
object.addProperty( "bold", component.isBoldRaw() );
}
if ( component.isItalicRaw() != null )
{
object.addProperty( "italic", component.isItalicRaw() );
}
if ( component.isUnderlinedRaw() != null )
{
object.addProperty( "underlined", component.isUnderlinedRaw() );
}
if ( component.isStrikethroughRaw() != null )
{
object.addProperty( "strikethrough", component.isStrikethroughRaw() );
}
if ( component.isObfuscatedRaw() != null )
{
object.addProperty( "obfuscated", component.isObfuscatedRaw() );
}
if ( component.getColorRaw() != null )
{
object.addProperty( "color", component.getColorRaw().getName() );
}
ComponentStyleSerializer.serializeTo( component.getStyle(), object );
if ( component.getInsertion() != null )
{
object.addProperty( "insertion", component.getInsertion() );
@ -213,10 +133,6 @@ public class BaseComponentSerializer
object.add( "hoverEvent", hoverEvent );
}
if ( component.getFontRaw() != null )
{
object.addProperty( "font", component.getFontRaw() );
}
if ( component.getExtra() != null )
{
object.add( "extra", context.serialize( component.getExtra() ) );

View File

@ -13,6 +13,7 @@ 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;
@ -36,6 +37,7 @@ public class ComponentSerializer implements JsonDeserializer<BaseComponent>
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() ).
@ -118,11 +120,44 @@ public class ComponentSerializer implements JsonDeserializer<BaseComponent>
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 static 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 static ComponentStyle deserializeStyle(JsonElement jsonElement)
{
return gson.fromJson( jsonElement, ComponentStyle.class );
}
public static JsonElement toJson(BaseComponent component)
{
return gson.toJsonTree( component );
}
public static JsonElement toJson(ComponentStyle style)
{
return gson.toJsonTree( style );
}
public static String toString(Object object)
{
return gson.toJson( object );
@ -144,6 +179,11 @@ public class ComponentSerializer implements JsonDeserializer<BaseComponent>
}
}
public static String toString(ComponentStyle style)
{
return gson.toJson( style );
}
@Override
public BaseComponent deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException
{

View File

@ -0,0 +1,118 @@
package net.md_5.bungee.chat;
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.JsonPrimitive;
import com.google.gson.JsonSerializationContext;
import com.google.gson.JsonSerializer;
import java.lang.reflect.Type;
import net.md_5.bungee.api.ChatColor;
import net.md_5.bungee.api.chat.ComponentStyle;
import net.md_5.bungee.api.chat.ComponentStyleBuilder;
public class ComponentStyleSerializer implements JsonSerializer<ComponentStyle>, JsonDeserializer<ComponentStyle>
{
private static boolean getAsBoolean(JsonElement el)
{
if ( el.isJsonPrimitive() )
{
JsonPrimitive primitive = (JsonPrimitive) el;
if ( primitive.isBoolean() )
{
return primitive.getAsBoolean();
}
if ( primitive.isNumber() )
{
Number number = primitive.getAsNumber();
if ( number instanceof Byte )
{
return number.byteValue() != 0;
}
}
}
return false;
}
static void serializeTo(ComponentStyle style, JsonObject object)
{
if ( style.isBoldRaw() != null )
{
object.addProperty( "bold", style.isBoldRaw() );
}
if ( style.isItalicRaw() != null )
{
object.addProperty( "italic", style.isItalicRaw() );
}
if ( style.isUnderlinedRaw() != null )
{
object.addProperty( "underlined", style.isUnderlinedRaw() );
}
if ( style.isStrikethroughRaw() != null )
{
object.addProperty( "strikethrough", style.isStrikethroughRaw() );
}
if ( style.isObfuscatedRaw() != null )
{
object.addProperty( "obfuscated", style.isObfuscatedRaw() );
}
if ( style.hasColor() )
{
object.addProperty( "color", style.getColor().getName() );
}
if ( style.hasFont() )
{
object.addProperty( "font", style.getFont() );
}
}
@Override
public ComponentStyle deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException
{
ComponentStyleBuilder builder = ComponentStyle.builder();
JsonObject object = json.getAsJsonObject();
if ( object.has( "bold" ) )
{
builder.bold( getAsBoolean( object.get( "bold" ) ) );
}
if ( object.has( "italic" ) )
{
builder.italic( getAsBoolean( object.get( "italic" ) ) );
}
if ( object.has( "underlined" ) )
{
builder.underlined( getAsBoolean( object.get( "underlined" ) ) );
}
if ( object.has( "strikethrough" ) )
{
builder.strikethrough( getAsBoolean( object.get( "strikethrough" ) ) );
}
if ( object.has( "obfuscated" ) )
{
builder.obfuscated( getAsBoolean( object.get( "obfuscated" ) ) );
}
if ( object.has( "color" ) )
{
builder.color( ChatColor.of( object.get( "color" ).getAsString() ) );
}
if ( object.has( "font" ) )
{
builder.font( object.get( "font" ).getAsString() );
}
return builder.build();
}
@Override
public JsonElement serialize(ComponentStyle src, Type typeOfSrc, JsonSerializationContext context)
{
JsonObject object = new JsonObject();
serializeTo( src, object );
return object;
}
}

View File

@ -8,6 +8,7 @@ import java.util.function.Function;
import java.util.function.ObjIntConsumer;
import java.util.function.Supplier;
import net.md_5.bungee.api.ChatColor;
import net.md_5.bungee.api.chat.hover.content.Entity;
import net.md_5.bungee.api.chat.hover.content.Text;
import net.md_5.bungee.chat.ComponentSerializer;
import org.junit.jupiter.api.Test;
@ -88,6 +89,14 @@ public class ComponentsTest
*/
}
@Test
public void testArrayUUIDParse()
{
BaseComponent[] uuidComponent = ComponentSerializer.parse( "{\"translate\":\"multiplayer.player.joined\",\"with\":[{\"text\":\"Rexcantor64\",\"hoverEvent\":{\"contents\":{\"type\":\"minecraft:player\",\"id\":[1328556382,-2138814985,-1895806765,-1039963041],\"name\":\"Rexcantor64\"},\"action\":\"show_entity\"},\"insertion\":\"Rexcantor64\",\"clickEvent\":{\"action\":\"suggest_command\",\"value\":\"/tell Rexcantor64 \"}}],\"color\":\"yellow\"}" );
assertEquals( "4f30295e-8084-45f7-8f00-48d3c2036c5f", ( (Entity) ( (TranslatableComponent) uuidComponent[0] ).getWith().get( 0 ).getHoverEvent().getContents().get( 0 ) ).getId() );
testDissembleReassemble( uuidComponent );
}
@Test
public void testEmptyComponentBuilderCreate()
{
@ -421,6 +430,16 @@ public class ComponentsTest
assertArrayEquals( component, reparsed );
}
@Test
public void testStyle()
{
ComponentStyle style = ComponentSerializer.deserializeStyle( "{\"color\":\"red\",\"font\":\"minecraft:example\",\"bold\":true,\"italic\":false,\"obfuscated\":true}" );
String text = ComponentSerializer.toString( style );
ComponentStyle reparsed = ComponentSerializer.deserializeStyle( text );
assertEquals( style, reparsed );
}
@Test
public void testBuilderAppendCreate()
{
@ -806,6 +825,28 @@ public class ComponentsTest
);
}
@Test
public void testHasFormatting()
{
BaseComponent component = new TextComponent();
assertFalse( component.hasFormatting() );
component.setBold( true );
assertTrue( component.hasFormatting() );
}
@Test
public void testStyleIsEmpty()
{
ComponentStyle style = ComponentStyle.builder().build();
assertTrue( style.isEmpty() );
style = ComponentStyle.builder()
.bold( true )
.build();
assertFalse( style.isEmpty() );
}
/*
* In legacy chat, colors and reset both reset all formatting.
* Make sure it works in combination with ComponentBuilder.

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.20-R0.2-SNAPSHOT</version>
<version>1.20-R0.3-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<groupId>net.md-5</groupId>
<artifactId>bungeecord-config</artifactId>
<version>1.20-R0.2-SNAPSHOT</version>
<version>1.20-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.20-R0.2-SNAPSHOT</version>
<version>1.20-R0.3-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<groupId>net.md-5</groupId>
<artifactId>bungeecord-event</artifactId>
<version>1.20-R0.2-SNAPSHOT</version>
<version>1.20-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.20-R0.2-SNAPSHOT</version>
<version>1.20-R0.3-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<groupId>net.md-5</groupId>
<artifactId>bungeecord-log</artifactId>
<version>1.20-R0.2-SNAPSHOT</version>
<version>1.20-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.20-R0.2-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<groupId>net.md-5</groupId>
<artifactId>bungeecord-module-cmd-alert</artifactId>
<version>1.20-R0.2-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.20-R0.2-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<groupId>net.md-5</groupId>
<artifactId>bungeecord-module-cmd-find</artifactId>
<version>1.20-R0.2-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.20-R0.2-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<groupId>net.md-5</groupId>
<artifactId>bungeecord-module-cmd-kick</artifactId>
<version>1.20-R0.2-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.20-R0.2-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<groupId>net.md-5</groupId>
<artifactId>bungeecord-module-cmd-list</artifactId>
<version>1.20-R0.2-SNAPSHOT</version>
<packaging>jar</packaging>
<name>cmd_list</name>
<description>Provides the glist command</description>
</project>

View File

@ -1,62 +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.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" );
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 );
sender.sendMessage( ProxyServer.getInstance().getTranslation( "command_list", server.getName(), players.size(), String.join( ChatColor.RESET + ", ", players ) ) );
}
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.20-R0.2-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<groupId>net.md-5</groupId>
<artifactId>bungeecord-module-cmd-send</artifactId>
<version>1.20-R0.2-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.20-R0.2-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<groupId>net.md-5</groupId>
<artifactId>bungeecord-module-cmd-server</artifactId>
<version>1.20-R0.2-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.20-R0.2-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<groupId>net.md-5</groupId>
<artifactId>bungeecord-module</artifactId>
<version>1.20-R0.2-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.20-R0.2-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<groupId>net.md-5</groupId>
<artifactId>bungeecord-module-reconnect-yaml</artifactId>
<version>1.20-R0.2-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.20-R0.2-SNAPSHOT</version>
<version>1.20-R0.3-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<groupId>net.md-5</groupId>
<artifactId>bungeecord-native</artifactId>
<version>1.20-R0.2-SNAPSHOT</version>
<version>1.20-R0.3-SNAPSHOT</version>
<packaging>jar</packaging>
<name>BungeeCord-Native</name>

21
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.20-R0.2-SNAPSHOT</version>
<version>1.20-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>
@ -83,7 +82,7 @@
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-bom</artifactId>
<version>4.1.100.Final</version>
<version>4.1.107.Final</version>
<type>pom</type>
<scope>import</scope>
</dependency>
@ -93,7 +92,7 @@
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.10.1</version>
<version>5.10.2</version>
<scope>test</scope>
</dependency>
<dependency>
@ -108,6 +107,12 @@
<version>3.0.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.jetbrains</groupId>
<artifactId>annotations-java5</artifactId>
<version>24.1.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
@ -122,7 +127,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>3.6.2</version>
<version>3.6.3</version>
</plugin>
</plugins>
</pluginManagement>
@ -132,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>
@ -146,7 +151,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.11.0</version>
<version>3.12.1</version>
</plugin>
<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.20-R0.2-SNAPSHOT</version>
<version>1.20-R0.3-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<groupId>net.md-5</groupId>
<artifactId>bungeecord-protocol</artifactId>
<version>1.20-R0.2-SNAPSHOT</version>
<version>1.20-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

@ -8,6 +8,8 @@ import net.md_5.bungee.protocol.packet.ClientCommand;
import net.md_5.bungee.protocol.packet.ClientSettings;
import net.md_5.bungee.protocol.packet.ClientStatus;
import net.md_5.bungee.protocol.packet.Commands;
import net.md_5.bungee.protocol.packet.CookieRequest;
import net.md_5.bungee.protocol.packet.CookieResponse;
import net.md_5.bungee.protocol.packet.EncryptionRequest;
import net.md_5.bungee.protocol.packet.EncryptionResponse;
import net.md_5.bungee.protocol.packet.EntityStatus;
@ -40,6 +42,7 @@ import net.md_5.bungee.protocol.packet.SetCompression;
import net.md_5.bungee.protocol.packet.StartConfiguration;
import net.md_5.bungee.protocol.packet.StatusRequest;
import net.md_5.bungee.protocol.packet.StatusResponse;
import net.md_5.bungee.protocol.packet.StoreCookie;
import net.md_5.bungee.protocol.packet.Subtitle;
import net.md_5.bungee.protocol.packet.SystemChat;
import net.md_5.bungee.protocol.packet.TabCompleteRequest;
@ -47,6 +50,7 @@ import net.md_5.bungee.protocol.packet.TabCompleteResponse;
import net.md_5.bungee.protocol.packet.Team;
import net.md_5.bungee.protocol.packet.Title;
import net.md_5.bungee.protocol.packet.TitleTimes;
import net.md_5.bungee.protocol.packet.Transfer;
import net.md_5.bungee.protocol.packet.ViewDistance;
public abstract class AbstractPacketHandler
@ -243,4 +247,20 @@ public abstract class AbstractPacketHandler
public void handle(FinishConfiguration finishConfiguration) throws Exception
{
}
public void handle(Transfer transfer) throws Exception
{
}
public void handle(StoreCookie storeCookie) throws Exception
{
}
public void handle(CookieRequest cookieRequest) throws Exception
{
}
public void handle(CookieResponse cookieResponse) throws Exception
{
}
}

View File

@ -19,6 +19,7 @@ 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;
@ -118,6 +119,14 @@ public abstract class DefinedPacket
}
}
public static ComponentStyle readComponentStyle(ByteBuf buf, int protocolVersion)
{
SpecificTag nbt = (SpecificTag) readTag( buf, protocolVersion );
JsonElement json = TagUtil.toJson( nbt );
return ComponentSerializer.deserializeStyle( json );
}
public static void writeEitherBaseComponent(Either<String, BaseComponent> message, ByteBuf buf, int protocolVersion)
{
if ( message.isLeft() )
@ -145,6 +154,14 @@ public abstract class DefinedPacket
}
}
public static void writeComponentStyle(ComponentStyle style, ByteBuf buf, int protocolVersion)
{
JsonElement json = ComponentSerializer.toJson( style );
SpecificTag nbt = TagUtil.fromJson( json );
writeTag( nbt, buf, protocolVersion );
}
public static void writeArray(byte[] b, ByteBuf buf)
{
if ( b.length > Short.MAX_VALUE )
@ -231,7 +248,7 @@ public abstract class DefinedPacket
if ( bytes > maxBytes )
{
throw new RuntimeException( "VarInt too big" );
throw new OverflowPacketException( "VarInt too big (max " + maxBytes + ")" );
}
if ( ( in & 0x80 ) != 0x80 )
@ -378,7 +395,7 @@ public abstract class DefinedPacket
case BLANK:
break;
case STYLED:
writeBaseComponent( (BaseComponent) format.getValue(), buf, protocolVersion ); // TODO: style
writeComponentStyle( (ComponentStyle) format.getValue(), buf, protocolVersion );
break;
case FIXED:
writeBaseComponent( (BaseComponent) format.getValue(), buf, protocolVersion );
@ -394,7 +411,7 @@ public abstract class DefinedPacket
case 0:
return new NumberFormat( NumberFormat.Type.BLANK, null );
case 1:
return new NumberFormat( NumberFormat.Type.STYLED, readBaseComponent( buf, protocolVersion ) ); // TODO: style
return new NumberFormat( NumberFormat.Type.STYLED, readComponentStyle( buf, protocolVersion ) );
case 2:
return new NumberFormat( NumberFormat.Type.FIXED, readBaseComponent( buf, protocolVersion ) );
default:

View File

@ -16,6 +16,8 @@ import net.md_5.bungee.protocol.packet.ClientChat;
import net.md_5.bungee.protocol.packet.ClientCommand;
import net.md_5.bungee.protocol.packet.ClientSettings;
import net.md_5.bungee.protocol.packet.Commands;
import net.md_5.bungee.protocol.packet.CookieRequest;
import net.md_5.bungee.protocol.packet.CookieResponse;
import net.md_5.bungee.protocol.packet.EncryptionRequest;
import net.md_5.bungee.protocol.packet.EncryptionResponse;
import net.md_5.bungee.protocol.packet.EntityStatus;
@ -46,6 +48,7 @@ import net.md_5.bungee.protocol.packet.SetCompression;
import net.md_5.bungee.protocol.packet.StartConfiguration;
import net.md_5.bungee.protocol.packet.StatusRequest;
import net.md_5.bungee.protocol.packet.StatusResponse;
import net.md_5.bungee.protocol.packet.StoreCookie;
import net.md_5.bungee.protocol.packet.Subtitle;
import net.md_5.bungee.protocol.packet.SystemChat;
import net.md_5.bungee.protocol.packet.TabCompleteRequest;
@ -53,6 +56,7 @@ import net.md_5.bungee.protocol.packet.TabCompleteResponse;
import net.md_5.bungee.protocol.packet.Team;
import net.md_5.bungee.protocol.packet.Title;
import net.md_5.bungee.protocol.packet.TitleTimes;
import net.md_5.bungee.protocol.packet.Transfer;
import net.md_5.bungee.protocol.packet.ViewDistance;
public enum Protocol
@ -90,7 +94,8 @@ public enum Protocol
map( ProtocolConstants.MINECRAFT_1_19_1, 0x20 ),
map( ProtocolConstants.MINECRAFT_1_19_3, 0x1F ),
map( ProtocolConstants.MINECRAFT_1_19_4, 0x23 ),
map( ProtocolConstants.MINECRAFT_1_20_2, 0x24 )
map( ProtocolConstants.MINECRAFT_1_20_2, 0x24 ),
map( ProtocolConstants.MINECRAFT_1_20_5, 0x26 )
);
TO_CLIENT.registerPacket(
Login.class,
@ -106,7 +111,8 @@ public enum Protocol
map( ProtocolConstants.MINECRAFT_1_19_1, 0x25 ),
map( ProtocolConstants.MINECRAFT_1_19_3, 0x24 ),
map( ProtocolConstants.MINECRAFT_1_19_4, 0x28 ),
map( ProtocolConstants.MINECRAFT_1_20_2, 0x29 )
map( ProtocolConstants.MINECRAFT_1_20_2, 0x29 ),
map( ProtocolConstants.MINECRAFT_1_20_5, 0x2B )
);
TO_CLIENT.registerPacket( Chat.class,
Chat::new,
@ -136,7 +142,8 @@ public enum Protocol
map( ProtocolConstants.MINECRAFT_1_19_3, 0x3D ),
map( ProtocolConstants.MINECRAFT_1_19_4, 0x41 ),
map( ProtocolConstants.MINECRAFT_1_20_2, 0x43 ),
map( ProtocolConstants.MINECRAFT_1_20_3, 0x45 )
map( ProtocolConstants.MINECRAFT_1_20_3, 0x45 ),
map( ProtocolConstants.MINECRAFT_1_20_5, 0x47 )
);
TO_CLIENT.registerPacket(
BossBar.class,
@ -195,7 +202,8 @@ public enum Protocol
map( ProtocolConstants.MINECRAFT_1_19_3, 0x54 ),
map( ProtocolConstants.MINECRAFT_1_19_4, 0x58 ),
map( ProtocolConstants.MINECRAFT_1_20_2, 0x5A ),
map( ProtocolConstants.MINECRAFT_1_20_3, 0x5C )
map( ProtocolConstants.MINECRAFT_1_20_3, 0x5C ),
map( ProtocolConstants.MINECRAFT_1_20_5, 0x5E )
);
TO_CLIENT.registerPacket(
ScoreboardScore.class,
@ -212,12 +220,14 @@ public enum Protocol
map( ProtocolConstants.MINECRAFT_1_19_3, 0x57 ),
map( ProtocolConstants.MINECRAFT_1_19_4, 0x5B ),
map( ProtocolConstants.MINECRAFT_1_20_2, 0x5D ),
map( ProtocolConstants.MINECRAFT_1_20_3, 0x5F )
map( ProtocolConstants.MINECRAFT_1_20_3, 0x5F ),
map( ProtocolConstants.MINECRAFT_1_20_5, 0x61 )
);
TO_CLIENT.registerPacket(
ScoreboardScoreReset.class,
ScoreboardScoreReset::new,
map( ProtocolConstants.MINECRAFT_1_20_3, 0x42 )
map( ProtocolConstants.MINECRAFT_1_20_3, 0x42 ),
map( ProtocolConstants.MINECRAFT_1_20_5, 0x44 )
);
TO_CLIENT.registerPacket(
ScoreboardDisplay.class,
@ -234,7 +244,8 @@ public enum Protocol
map( ProtocolConstants.MINECRAFT_1_19_3, 0x4D ),
map( ProtocolConstants.MINECRAFT_1_19_4, 0x51 ),
map( ProtocolConstants.MINECRAFT_1_20_2, 0x53 ),
map( ProtocolConstants.MINECRAFT_1_20_3, 0x55 )
map( ProtocolConstants.MINECRAFT_1_20_3, 0x55 ),
map( ProtocolConstants.MINECRAFT_1_20_5, 0x57 )
);
TO_CLIENT.registerPacket(
Team.class,
@ -251,7 +262,8 @@ public enum Protocol
map( ProtocolConstants.MINECRAFT_1_19_3, 0x56 ),
map( ProtocolConstants.MINECRAFT_1_19_4, 0x5A ),
map( ProtocolConstants.MINECRAFT_1_20_2, 0x5C ),
map( ProtocolConstants.MINECRAFT_1_20_3, 0x5E )
map( ProtocolConstants.MINECRAFT_1_20_3, 0x5E ),
map( ProtocolConstants.MINECRAFT_1_20_5, 0x60 )
);
TO_CLIENT.registerPacket(
PluginMessage.class,
@ -268,7 +280,8 @@ public enum Protocol
map( ProtocolConstants.MINECRAFT_1_19_1, 0x16 ),
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_2, 0x18 ),
map( ProtocolConstants.MINECRAFT_1_20_5, 0x19 )
);
TO_CLIENT.registerPacket(
Kick.class,
@ -285,7 +298,8 @@ public enum Protocol
map( ProtocolConstants.MINECRAFT_1_19_1, 0x19 ),
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_2, 0x1B ),
map( ProtocolConstants.MINECRAFT_1_20_5, 0x1D )
);
TO_CLIENT.registerPacket(
Title.class,
@ -303,7 +317,8 @@ public enum Protocol
map( ProtocolConstants.MINECRAFT_1_19_3, 0x5B ),
map( ProtocolConstants.MINECRAFT_1_19_4, 0x5F ),
map( ProtocolConstants.MINECRAFT_1_20_2, 0x61 ),
map( ProtocolConstants.MINECRAFT_1_20_3, 0x63 )
map( ProtocolConstants.MINECRAFT_1_20_3, 0x63 ),
map( ProtocolConstants.MINECRAFT_1_20_5, 0x65 )
);
TO_CLIENT.registerPacket(
ClearTitles.class,
@ -323,7 +338,8 @@ public enum Protocol
map( ProtocolConstants.MINECRAFT_1_19_3, 0x59 ),
map( ProtocolConstants.MINECRAFT_1_19_4, 0x5D ),
map( ProtocolConstants.MINECRAFT_1_20_2, 0x5F ),
map( ProtocolConstants.MINECRAFT_1_20_3, 0x61 )
map( ProtocolConstants.MINECRAFT_1_20_3, 0x61 ),
map( ProtocolConstants.MINECRAFT_1_20_5, 0x63 )
);
TO_CLIENT.registerPacket(
TitleTimes.class,
@ -334,7 +350,8 @@ public enum Protocol
map( ProtocolConstants.MINECRAFT_1_19_3, 0x5C ),
map( ProtocolConstants.MINECRAFT_1_19_4, 0x60 ),
map( ProtocolConstants.MINECRAFT_1_20_2, 0x62 ),
map( ProtocolConstants.MINECRAFT_1_20_3, 0x64 )
map( ProtocolConstants.MINECRAFT_1_20_3, 0x64 ),
map( ProtocolConstants.MINECRAFT_1_20_5, 0x66 )
);
TO_CLIENT.registerPacket(
SystemChat.class,
@ -344,7 +361,8 @@ public enum Protocol
map( ProtocolConstants.MINECRAFT_1_19_3, 0x60 ),
map( ProtocolConstants.MINECRAFT_1_19_4, 0x64 ),
map( ProtocolConstants.MINECRAFT_1_20_2, 0x67 ),
map( ProtocolConstants.MINECRAFT_1_20_3, 0x69 )
map( ProtocolConstants.MINECRAFT_1_20_3, 0x69 ),
map( ProtocolConstants.MINECRAFT_1_20_5, 0x6C )
);
TO_CLIENT.registerPacket(
PlayerListHeaderFooter.class,
@ -365,7 +383,8 @@ public enum Protocol
map( ProtocolConstants.MINECRAFT_1_19_3, 0x61 ),
map( ProtocolConstants.MINECRAFT_1_19_4, 0x65 ),
map( ProtocolConstants.MINECRAFT_1_20_2, 0x68 ),
map( ProtocolConstants.MINECRAFT_1_20_3, 0x6A )
map( ProtocolConstants.MINECRAFT_1_20_3, 0x6A ),
map( ProtocolConstants.MINECRAFT_1_20_5, 0x6D )
);
TO_CLIENT.registerPacket(
EntityStatus.class,
@ -382,7 +401,8 @@ public enum Protocol
map( ProtocolConstants.MINECRAFT_1_19_1, 0x1A ),
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_2, 0x1D ),
map( ProtocolConstants.MINECRAFT_1_20_5, 0x1F )
);
TO_CLIENT.registerPacket(
Commands.class,
@ -408,7 +428,8 @@ public enum Protocol
map( ProtocolConstants.MINECRAFT_1_19_1, 0x1D ),
map( ProtocolConstants.MINECRAFT_1_19_3, 0x1C ),
map( ProtocolConstants.MINECRAFT_1_19_4, 0x1F ),
map( ProtocolConstants.MINECRAFT_1_20_2, 0x20 )
map( ProtocolConstants.MINECRAFT_1_20_2, 0x20 ),
map( ProtocolConstants.MINECRAFT_1_20_5, 0x22 )
);
TO_CLIENT.registerPacket(
ViewDistance.class,
@ -422,7 +443,8 @@ public enum Protocol
map( ProtocolConstants.MINECRAFT_1_19_3, 0x4B ),
map( ProtocolConstants.MINECRAFT_1_19_4, 0x4F ),
map( ProtocolConstants.MINECRAFT_1_20_2, 0x51 ),
map( ProtocolConstants.MINECRAFT_1_20_3, 0x53 )
map( ProtocolConstants.MINECRAFT_1_20_3, 0x53 ),
map( ProtocolConstants.MINECRAFT_1_20_5, 0x62 )
);
TO_CLIENT.registerPacket(
ServerData.class,
@ -432,27 +454,46 @@ public enum Protocol
map( ProtocolConstants.MINECRAFT_1_19_3, 0x41 ),
map( ProtocolConstants.MINECRAFT_1_19_4, 0x45 ),
map( ProtocolConstants.MINECRAFT_1_20_2, 0x47 ),
map( ProtocolConstants.MINECRAFT_1_20_3, 0x49 )
map( ProtocolConstants.MINECRAFT_1_20_3, 0x49 ),
map( ProtocolConstants.MINECRAFT_1_20_5, 0x4B )
);
TO_CLIENT.registerPacket(
PlayerListItemRemove.class,
PlayerListItemRemove::new,
map( ProtocolConstants.MINECRAFT_1_19_3, 0x35 ),
map( ProtocolConstants.MINECRAFT_1_19_4, 0x39 ),
map( ProtocolConstants.MINECRAFT_1_20_2, 0x3B )
map( ProtocolConstants.MINECRAFT_1_20_2, 0x3B ),
map( ProtocolConstants.MINECRAFT_1_20_5, 0x3D )
);
TO_CLIENT.registerPacket(
PlayerListItemUpdate.class,
PlayerListItemUpdate::new,
map( ProtocolConstants.MINECRAFT_1_19_3, 0x36 ),
map( ProtocolConstants.MINECRAFT_1_19_4, 0x3A ),
map( ProtocolConstants.MINECRAFT_1_20_2, 0x3C )
map( ProtocolConstants.MINECRAFT_1_20_2, 0x3C ),
map( ProtocolConstants.MINECRAFT_1_20_5, 0x3E )
);
TO_CLIENT.registerPacket(
StartConfiguration.class,
StartConfiguration::new,
map( ProtocolConstants.MINECRAFT_1_20_2, 0x65 ),
map( ProtocolConstants.MINECRAFT_1_20_3, 0x67 )
map( ProtocolConstants.MINECRAFT_1_20_3, 0x67 ),
map( ProtocolConstants.MINECRAFT_1_20_5, 0x69 )
);
TO_CLIENT.registerPacket(
CookieRequest.class,
CookieRequest::new,
map( ProtocolConstants.MINECRAFT_1_20_5, 0x16 )
);
TO_CLIENT.registerPacket(
StoreCookie.class,
StoreCookie::new,
map( ProtocolConstants.MINECRAFT_1_20_5, 0x6B )
);
TO_CLIENT.registerPacket(
Transfer.class,
Transfer::new,
map( ProtocolConstants.MINECRAFT_1_20_5, 0x73 )
);
TO_SERVER.registerPacket(
@ -471,7 +512,8 @@ public enum Protocol
map( ProtocolConstants.MINECRAFT_1_19_3, 0x11 ),
map( ProtocolConstants.MINECRAFT_1_19_4, 0x12 ),
map( ProtocolConstants.MINECRAFT_1_20_2, 0x14 ),
map( ProtocolConstants.MINECRAFT_1_20_3, 0x15 )
map( ProtocolConstants.MINECRAFT_1_20_3, 0x15 ),
map( ProtocolConstants.MINECRAFT_1_20_5, 0x17 )
);
TO_SERVER.registerPacket( Chat.class,
Chat::new,
@ -538,13 +580,19 @@ public enum Protocol
map( ProtocolConstants.MINECRAFT_1_19_3, 0x0C ),
map( ProtocolConstants.MINECRAFT_1_19_4, 0x0D ),
map( ProtocolConstants.MINECRAFT_1_20_2, 0x0F ),
map( ProtocolConstants.MINECRAFT_1_20_3, 0x10 )
map( ProtocolConstants.MINECRAFT_1_20_3, 0x10 ),
map( ProtocolConstants.MINECRAFT_1_20_5, 0x11 )
);
TO_SERVER.registerPacket(
StartConfiguration.class,
StartConfiguration::new,
map( ProtocolConstants.MINECRAFT_1_20_2, 0x0B )
);
TO_SERVER.registerPacket(
CookieResponse.class,
CookieResponse::new,
map( ProtocolConstants.MINECRAFT_1_20_5, 0x10 )
);
}
},
// 1
@ -605,6 +653,11 @@ public enum Protocol
LoginPayloadRequest::new,
map( ProtocolConstants.MINECRAFT_1_13, 0x04 )
);
TO_CLIENT.registerPacket(
CookieRequest.class,
CookieRequest::new,
map( ProtocolConstants.MINECRAFT_1_20_5, 0x05 )
);
TO_SERVER.registerPacket(
LoginRequest.class,
@ -626,6 +679,11 @@ public enum Protocol
LoginAcknowledged::new,
map( ProtocolConstants.MINECRAFT_1_20_2, 0x03 )
);
TO_SERVER.registerPacket(
CookieResponse.class,
CookieResponse::new,
map( ProtocolConstants.MINECRAFT_1_20_5, 0x04 )
);
}
},
// 3
@ -633,25 +691,45 @@ public enum Protocol
{
{
TO_CLIENT.registerPacket(
CookieRequest.class,
CookieRequest::new,
map( ProtocolConstants.MINECRAFT_1_20_5, 0x00 )
);
TO_CLIENT.registerPacket(
PluginMessage.class,
PluginMessage::new,
map( ProtocolConstants.MINECRAFT_1_20_2, 0x00 )
map( ProtocolConstants.MINECRAFT_1_20_2, 0x00 ),
map( ProtocolConstants.MINECRAFT_1_20_5, 0x01 )
);
TO_CLIENT.registerPacket(
Kick.class,
Kick::new,
map( ProtocolConstants.MINECRAFT_1_20_2, 0x01 )
map( ProtocolConstants.MINECRAFT_1_20_2, 0x01 ),
map( ProtocolConstants.MINECRAFT_1_20_5, 0x02 )
);
TO_CLIENT.registerPacket(
FinishConfiguration.class,
FinishConfiguration::new,
map( ProtocolConstants.MINECRAFT_1_20_2, 0x02 )
map( ProtocolConstants.MINECRAFT_1_20_2, 0x02 ),
map( ProtocolConstants.MINECRAFT_1_20_5, 0x03 )
);
TO_CLIENT.registerPacket(
KeepAlive.class,
KeepAlive::new,
map( ProtocolConstants.MINECRAFT_1_20_2, 0x03 )
map( ProtocolConstants.MINECRAFT_1_20_2, 0x03 ),
map( ProtocolConstants.MINECRAFT_1_20_5, 0x04 )
);
TO_CLIENT.registerPacket(
StoreCookie.class,
StoreCookie::new,
map( ProtocolConstants.MINECRAFT_1_20_5, 0x09 )
);
TO_CLIENT.registerPacket(
Transfer.class,
Transfer::new,
map( ProtocolConstants.MINECRAFT_1_20_5, 0x0A )
);
TO_SERVER.registerPacket(
@ -662,17 +740,20 @@ public enum Protocol
TO_SERVER.registerPacket(
PluginMessage.class,
PluginMessage::new,
map( ProtocolConstants.MINECRAFT_1_20_2, 0x01 )
map( ProtocolConstants.MINECRAFT_1_20_2, 0x01 ),
map( ProtocolConstants.MINECRAFT_1_20_5, 0x02 )
);
TO_SERVER.registerPacket(
FinishConfiguration.class,
FinishConfiguration::new,
map( ProtocolConstants.MINECRAFT_1_20_2, 0x02 )
map( ProtocolConstants.MINECRAFT_1_20_2, 0x02 ),
map( ProtocolConstants.MINECRAFT_1_20_5, 0x03 )
);
TO_SERVER.registerPacket(
KeepAlive.class,
KeepAlive::new,
map( ProtocolConstants.MINECRAFT_1_20_2, 0x03 )
map( ProtocolConstants.MINECRAFT_1_20_2, 0x03 ),
map( ProtocolConstants.MINECRAFT_1_20_5, 0x04 )
);
}
};

View File

@ -45,6 +45,7 @@ public class ProtocolConstants
public static final int MINECRAFT_1_20 = 763;
public static final int MINECRAFT_1_20_2 = 764;
public static final int MINECRAFT_1_20_3 = 765;
public static final int MINECRAFT_1_20_5 = 1073742000;
public static final List<String> SUPPORTED_VERSIONS;
public static final List<Integer> SUPPORTED_VERSION_IDS;
@ -109,7 +110,7 @@ public class ProtocolConstants
if ( SNAPSHOT_SUPPORT )
{
// supportedVersions.add( "1.20.x" );
// supportedVersionIds.add( ProtocolConstants.MINECRAFT_1_20_3 );
supportedVersionIds.add( ProtocolConstants.MINECRAFT_1_20_5 );
}
SUPPORTED_VERSIONS = supportedVersions.build();

View File

@ -124,9 +124,11 @@ public final class TagUtil
for ( JsonElement jsonEl : jsonArray )
{
SpecificTag subTag = fromJson( jsonEl );
if ( subTag.tagType() != listType )
if ( !( subTag instanceof CompoundTag ) )
{
throw new IllegalArgumentException( "Cannot convert mixed JsonArray to Tag" );
CompoundTag wrapper = new CompoundTag();
wrapper.add( "", subTag );
subTag = wrapper;
}
tagItems.add( subTag );
@ -179,6 +181,20 @@ public final class TagUtil
JsonArray jsonList = new JsonArray( items.size() );
for ( SpecificTag subTag : items )
{
if ( subTag instanceof CompoundTag )
{
CompoundTag compound = (CompoundTag) subTag;
if ( compound.size() == 1 )
{
SpecificTag first = (SpecificTag) compound.get( "" );
if ( !first.isError() )
{
jsonList.add( toJson( first ) );
continue;
}
}
}
jsonList.add( toJson( subTag ) );
}

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<>();
@ -326,18 +327,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>()
@ -354,7 +366,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)
@ -382,8 +394,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)
@ -411,8 +435,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)
@ -440,6 +476,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>()
{
@ -455,7 +503,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)
@ -483,6 +531,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>()
{
@ -538,11 +598,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 );
@ -811,6 +880,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;
@ -937,9 +1404,13 @@ 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) provider ).key;
return ( (DummyProvider) ASK_SERVER ).key;
}
@Data

View File

@ -0,0 +1,38 @@
package net.md_5.bungee.protocol.packet;
import io.netty.buffer.ByteBuf;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import net.md_5.bungee.protocol.AbstractPacketHandler;
import net.md_5.bungee.protocol.DefinedPacket;
import net.md_5.bungee.protocol.ProtocolConstants;
@Data
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode(callSuper = false)
public class CookieRequest extends DefinedPacket
{
private String cookie;
@Override
public void read(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion)
{
cookie = readString( buf );
}
@Override
public void write(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion)
{
writeString( cookie, buf );
}
@Override
public void handle(AbstractPacketHandler handler) throws Exception
{
handler.handle( this );
}
}

View File

@ -0,0 +1,41 @@
package net.md_5.bungee.protocol.packet;
import io.netty.buffer.ByteBuf;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import net.md_5.bungee.protocol.AbstractPacketHandler;
import net.md_5.bungee.protocol.DefinedPacket;
import net.md_5.bungee.protocol.ProtocolConstants;
@Data
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode(callSuper = false)
public class CookieResponse extends DefinedPacket
{
private String cookie;
private byte[] data;
@Override
public void read(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion)
{
cookie = readString( buf );
data = readNullable( DefinedPacket::readArray, buf );
}
@Override
public void write(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion)
{
writeString( cookie, buf );
writeNullable( data, DefinedPacket::writeArray, buf );
}
@Override
public void handle(AbstractPacketHandler handler) throws Exception
{
handler.handle( this );
}
}

View File

@ -19,6 +19,7 @@ public class EncryptionRequest extends DefinedPacket
private String serverId;
private byte[] publicKey;
private byte[] verifyToken;
private boolean shouldAuthenticate;
@Override
public void read(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion)
@ -26,6 +27,10 @@ public class EncryptionRequest extends DefinedPacket
serverId = readString( buf );
publicKey = readArray( buf );
verifyToken = readArray( buf );
if ( protocolVersion >= ProtocolConstants.MINECRAFT_1_20_5 )
{
shouldAuthenticate = buf.readBoolean();
}
}
@Override
@ -34,6 +39,10 @@ public class EncryptionRequest extends DefinedPacket
writeString( serverId, buf );
writeArray( publicKey, buf );
writeArray( verifyToken, buf );
if ( protocolVersion >= ProtocolConstants.MINECRAFT_1_20_5 )
{
buf.writeBoolean( shouldAuthenticate );
}
}
@Override

View File

@ -41,6 +41,7 @@ public class Login extends DefinedPacket
private boolean flat;
private Location deathLocation;
private int portalCooldown;
private boolean secureProfile;
@Override
public void read(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion)
@ -154,6 +155,11 @@ public class Login extends DefinedPacket
{
portalCooldown = readVarInt( buf );
}
if ( protocolVersion >= ProtocolConstants.MINECRAFT_1_20_5 )
{
secureProfile = buf.readBoolean();
}
}
@Override
@ -275,6 +281,11 @@ public class Login extends DefinedPacket
{
writeVarInt( portalCooldown, buf );
}
if ( protocolVersion >= ProtocolConstants.MINECRAFT_1_20_5 )
{
buf.writeBoolean( secureProfile );
}
}
@Override

View File

@ -33,7 +33,7 @@ public class ServerData extends DefinedPacket
{
if ( protocolVersion >= ProtocolConstants.MINECRAFT_1_19_4 )
{
icon = DefinedPacket.readArray( buf );
icon = readArray( buf );
} else
{
icon = readString( buf );
@ -45,7 +45,7 @@ public class ServerData extends DefinedPacket
preview = buf.readBoolean();
}
if ( protocolVersion >= ProtocolConstants.MINECRAFT_1_19_1 )
if ( protocolVersion >= ProtocolConstants.MINECRAFT_1_19_1 && protocolVersion < ProtocolConstants.MINECRAFT_1_20_5 )
{
enforceSecure = buf.readBoolean();
}
@ -76,7 +76,7 @@ public class ServerData extends DefinedPacket
buf.writeBoolean( true );
if ( protocolVersion >= ProtocolConstants.MINECRAFT_1_19_4 )
{
DefinedPacket.writeArray( (byte[]) icon, buf );
writeArray( (byte[]) icon, buf );
} else
{
writeString( (String) icon, buf );
@ -91,7 +91,7 @@ public class ServerData extends DefinedPacket
buf.writeBoolean( preview );
}
if ( protocolVersion >= ProtocolConstants.MINECRAFT_1_19_1 )
if ( protocolVersion >= ProtocolConstants.MINECRAFT_1_19_1 && protocolVersion < ProtocolConstants.MINECRAFT_1_20_5 )
{
buf.writeBoolean( enforceSecure );
}

View File

@ -0,0 +1,41 @@
package net.md_5.bungee.protocol.packet;
import io.netty.buffer.ByteBuf;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import net.md_5.bungee.protocol.AbstractPacketHandler;
import net.md_5.bungee.protocol.DefinedPacket;
import net.md_5.bungee.protocol.ProtocolConstants;
@Data
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode(callSuper = false)
public class StoreCookie extends DefinedPacket
{
private String key;
private byte[] data;
@Override
public void read(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion)
{
key = readString( buf );
data = readArray( buf, 5120 );
}
@Override
public void write(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion)
{
writeString( key, buf );
writeArray( data, buf );
}
@Override
public void handle(AbstractPacketHandler handler) throws Exception
{
handler.handle( this );
}
}

View File

@ -0,0 +1,41 @@
package net.md_5.bungee.protocol.packet;
import io.netty.buffer.ByteBuf;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import net.md_5.bungee.protocol.AbstractPacketHandler;
import net.md_5.bungee.protocol.DefinedPacket;
import net.md_5.bungee.protocol.ProtocolConstants;
@Data
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode(callSuper = false)
public class Transfer extends DefinedPacket
{
private String host;
private int port;
@Override
public void read(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion)
{
host = readString( buf );
port = readVarInt( buf );
}
@Override
public void write(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion)
{
writeString( host, buf );
writeVarInt( port, buf );
}
@Override
public void handle(AbstractPacketHandler handler) throws Exception
{
handler.handle( this );
}
}

View File

@ -0,0 +1,29 @@
package net.md_5.bungee.protocol;
import static org.junit.jupiter.api.Assertions.*;
import com.google.gson.Gson;
import com.google.gson.JsonElement;
import org.junit.jupiter.api.Test;
import se.llbit.nbt.SpecificTag;
public class TagUtilTest
{
private static final Gson GSON = new Gson();
private static void testDissembleReassemble(String json)
{
JsonElement parsedJson = GSON.fromJson( json, JsonElement.class );
SpecificTag nbt = TagUtil.fromJson( parsedJson );
JsonElement convertedElement = TagUtil.toJson( nbt );
String convertedJson = GSON.toJson( convertedElement );
assertEquals( json, convertedJson );
}
@Test
public void testStringLiteral()
{
testDissembleReassemble( "{\"text\":\"\",\"extra\":[\"hello\",{\"text\":\"there\",\"color\":\"#ff0000\"},{\"text\":\"friend\",\"font\":\"minecraft:default\"}]}" );
}
}

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.20-R0.2-SNAPSHOT</version>
<version>1.20-R0.3-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<groupId>net.md-5</groupId>
<artifactId>bungeecord-proxy</artifactId>
<version>1.20-R0.2-SNAPSHOT</version>
<version>1.20-R0.3-SNAPSHOT</version>
<packaging>jar</packaging>
<name>BungeeCord-Proxy</name>
@ -52,37 +51,37 @@
<scope>compile</scope>
</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>
@ -96,26 +95,26 @@
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<version>8.1.0</version>
<version>8.2.0</version>
<scope>runtime</scope>
</dependency>
<!-- add these back in as they are not exposed by the API -->
<dependency>
<groupId>org.apache.maven</groupId>
<artifactId>maven-resolver-provider</artifactId>
<version>3.8.5</version>
<version>3.9.6</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.apache.maven.resolver</groupId>
<artifactId>maven-resolver-connector-basic</artifactId>
<version>1.7.3</version>
<version>1.9.18</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.apache.maven.resolver</groupId>
<artifactId>maven-resolver-transport-http</artifactId>
<version>1.7.3</version>
<version>1.9.18</version>
<scope>runtime</scope>
</dependency>
</dependencies>

View File

@ -58,6 +58,7 @@ 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;
@ -70,6 +71,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.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;
@ -89,7 +91,6 @@ 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;
@ -169,11 +170,11 @@ public class BungeeCord extends ProxyServer
.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
@ -271,10 +272,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 );

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

@ -73,7 +73,8 @@ public class EncryptionUtil
byte[] pubKey = keys.getPublic().getEncoded();
byte[] verify = new byte[ 4 ];
random.nextBytes( verify );
return new EncryptionRequest( hash, pubKey, verify );
// always auth for now
return new EncryptionRequest( hash, pubKey, verify, true );
}
public static boolean check(PlayerPublicKey publicKey, UUID uuid) throws GeneralSecurityException

View File

@ -10,6 +10,7 @@ import java.util.Locale;
import java.util.Queue;
import java.util.Set;
import java.util.UUID;
import java.util.logging.Level;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import net.md_5.bungee.api.ChatColor;
@ -39,6 +40,8 @@ import net.md_5.bungee.protocol.Either;
import net.md_5.bungee.protocol.PacketWrapper;
import net.md_5.bungee.protocol.Protocol;
import net.md_5.bungee.protocol.ProtocolConstants;
import net.md_5.bungee.protocol.packet.CookieRequest;
import net.md_5.bungee.protocol.packet.CookieResponse;
import net.md_5.bungee.protocol.packet.EncryptionRequest;
import net.md_5.bungee.protocol.packet.EntityStatus;
import net.md_5.bungee.protocol.packet.GameState;
@ -187,6 +190,12 @@ public class ServerConnector extends PacketHandler
ch.setCompressionThreshold( setCompression.getThreshold() );
}
@Override
public void handle(CookieRequest cookieRequest) throws Exception
{
user.retrieveCookie( cookieRequest.getCookie() ).thenAccept( (cookie) -> ch.write( new CookieResponse( cookieRequest.getCookie(), cookie ) ) );
}
@Override
public void handle(Login login) throws Exception
{
@ -243,7 +252,7 @@ public class ServerConnector extends PacketHandler
// Set tab list size, TODO: what shall we do about packet mutability
Login modLogin = new Login( login.getEntityId(), login.isHardcore(), login.getGameMode(), login.getPreviousGameMode(), login.getWorldNames(), login.getDimensions(), login.getDimension(), login.getWorldName(), login.getSeed(), login.getDifficulty(),
(byte) user.getPendingConnection().getListener().getTabListSize(), login.getLevelType(), login.getViewDistance(), login.getSimulationDistance(), login.isReducedDebugInfo(), login.isNormalRespawn(), login.isLimitedCrafting(), login.isDebug(), login.isFlat(), login.getDeathLocation(),
login.getPortalCooldown() );
login.getPortalCooldown(), login.isSecureProfile() );
user.unsafe().sendPacket( modLogin );
@ -339,6 +348,14 @@ public class ServerConnector extends PacketHandler
private void cutThrough(ServerConnection server)
{
// TODO: Fix this?
if ( !user.isActive() )
{
server.disconnect( "Quitting" );
bungee.getLogger().log( Level.WARNING, "[{0}] No client connected for pending server!", user );
return;
}
if ( user.getPendingConnection().getVersion() >= ProtocolConstants.MINECRAFT_1_20_2 )
{
if ( user.getServer() != null )
@ -360,15 +377,6 @@ public class ServerConnector extends PacketHandler
user.getServer().disconnect( "Quitting" );
}
// TODO: Fix this?
if ( !user.isActive() )
{
server.disconnect( "Quitting" );
// Silly server admins see stack trace and die
bungee.getLogger().warning( "No client connected for pending server!" );
return;
}
// Add to new server
// TODO: Move this to the connected() method of DownstreamBridge
target.addPlayer( user );

View File

@ -20,6 +20,7 @@ import java.util.Map;
import java.util.Objects;
import java.util.Queue;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.logging.Level;
import lombok.Getter;
@ -60,7 +61,9 @@ import net.md_5.bungee.protocol.packet.Kick;
import net.md_5.bungee.protocol.packet.PlayerListHeaderFooter;
import net.md_5.bungee.protocol.packet.PluginMessage;
import net.md_5.bungee.protocol.packet.SetCompression;
import net.md_5.bungee.protocol.packet.StoreCookie;
import net.md_5.bungee.protocol.packet.SystemChat;
import net.md_5.bungee.protocol.packet.Transfer;
import net.md_5.bungee.tab.ServerUnique;
import net.md_5.bungee.tab.TabList;
import net.md_5.bungee.util.CaseInsensitiveSet;
@ -771,4 +774,26 @@ public final class UserConnection implements ProxiedPlayer
{
return serverSentScoreboard;
}
@Override
public CompletableFuture<byte[]> retrieveCookie(String cookie)
{
return pendingConnection.retrieveCookie( cookie );
}
@Override
public void storeCookie(String cookie, byte[] data)
{
Preconditions.checkState( getPendingConnection().getVersion() >= ProtocolConstants.MINECRAFT_1_20_5, "Cookies are only supported in 1.20.5 and above" );
unsafe().sendPacket( new StoreCookie( cookie, data ) );
}
@Override
public void transfer(String host, int port)
{
Preconditions.checkState( getPendingConnection().getVersion() >= ProtocolConstants.MINECRAFT_1_20_5, "Transfers are only supported in 1.20.5 and above" );
unsafe().sendPacket( new Transfer( host, port ) );
}
}

View File

@ -68,6 +68,7 @@ public class Configuration implements ProxyConfig
private int compressionThreshold = 256;
private boolean preventProxyConnections;
private boolean forgeSupport;
private boolean rejectTransfers;
public void load()
{
@ -103,6 +104,7 @@ public class Configuration implements ProxyConfig
compressionThreshold = adapter.getInt( "network_compression_threshold", compressionThreshold );
preventProxyConnections = adapter.getBoolean( "prevent_proxy_connections", preventProxyConnections );
forgeSupport = adapter.getBoolean( "forge_support", forgeSupport );
rejectTransfers = adapter.getBoolean( "reject_transfers", rejectTransfers );
disabledCommands = new CaseInsensitiveSet( (Collection<String>) adapter.getList( "disabled_commands", Arrays.asList( "disabledcommandhere" ) ) );

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;
@ -312,7 +317,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
@ -745,6 +750,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 ) )
@ -761,11 +771,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

@ -11,12 +11,19 @@ import java.security.GeneralSecurityException;
import java.security.MessageDigest;
import java.time.Instant;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.Queue;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import java.util.logging.Level;
import javax.crypto.SecretKey;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.ToString;
import net.md_5.bungee.BungeeCord;
import net.md_5.bungee.BungeeServerInfo;
import net.md_5.bungee.EncryptionUtil;
@ -51,6 +58,8 @@ import net.md_5.bungee.protocol.PacketWrapper;
import net.md_5.bungee.protocol.PlayerPublicKey;
import net.md_5.bungee.protocol.Protocol;
import net.md_5.bungee.protocol.ProtocolConstants;
import net.md_5.bungee.protocol.packet.CookieRequest;
import net.md_5.bungee.protocol.packet.CookieResponse;
import net.md_5.bungee.protocol.packet.EncryptionRequest;
import net.md_5.bungee.protocol.packet.EncryptionResponse;
import net.md_5.bungee.protocol.packet.Handshake;
@ -86,6 +95,19 @@ public class InitialHandler extends PacketHandler implements PendingConnection
@Getter
private final Set<String> registeredChannels = new HashSet<>();
private State thisState = State.HANDSHAKE;
private final Queue<CookieFuture> requestedCookies = new LinkedList<>();
@Data
@ToString
@EqualsAndHashCode
@AllArgsConstructor
public static class CookieFuture
{
private String cookie;
private CompletableFuture<byte[]> future;
}
private final Unsafe unsafe = new Unsafe()
{
@Override
@ -109,8 +131,19 @@ public class InitialHandler extends PacketHandler implements PendingConnection
private boolean legacy;
@Getter
private String extraDataInHandshake = "";
@Getter
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
{
@ -349,6 +382,8 @@ public class InitialHandler extends PacketHandler implements PendingConnection
ch.setProtocol( Protocol.STATUS );
break;
case 2:
case 3:
transferred = handshake.getRequestedProtocol() == 3;
// Login
bungee.getLogger().log( Level.INFO, "{0} has connected", this );
thisState = State.USERNAME;
@ -365,6 +400,12 @@ public class InitialHandler extends PacketHandler implements PendingConnection
}
return;
}
if ( transferred && bungee.config.isRejectTransfers() )
{
disconnect( bungee.getTranslation( "reject_transfer" ) );
return;
}
break;
default:
throw new QuietException( "Cannot request protocol " + handshake.getRequestedProtocol() );
@ -408,6 +449,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 )
@ -416,14 +458,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>()
{
@ -440,6 +474,16 @@ public class InitialHandler extends PacketHandler implements PendingConnection
{
return;
}
if ( !realName.equals( name ) )
{
// Floodgate changes the name attribute with reflexion
setName( realName = name );
}
if ( uniqueId != null )
{
// if plugin called setUniqueId()
realId = uniqueId;
}
if ( onlineMode )
{
thisState = State.ENCRYPT;
@ -494,8 +538,8 @@ public class InitialHandler extends PacketHandler implements PendingConnection
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;
}
@ -513,10 +557,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;
}
}
if ( BungeeCord.getInstance().config.isEnforceSecureProfile() )
@ -621,6 +680,13 @@ public class InitialHandler extends PacketHandler implements PendingConnection
ch.getHandle().pipeline().get( HandlerBoss.class ).setHandler( new UpstreamBridge( bungee, userCon ) );
bungee.getPluginManager().callEvent( new PostLoginEvent( userCon ) );
// #3612: Don't progress further if disconnected during event
if ( ch.isClosed() )
{
return;
}
ServerInfo server;
if ( bungee.getReconnectHandler() != null )
{
@ -643,6 +709,34 @@ public class InitialHandler extends PacketHandler implements PendingConnection
disconnect( "Unexpected custom LoginPayloadResponse" );
}
@Override
public void handle(CookieResponse cookieResponse)
{
// be careful, backend server could also make the client send a cookie response
CookieFuture future;
synchronized ( requestedCookies )
{
future = requestedCookies.peek();
if ( future != null )
{
if ( future.cookie.equals( cookieResponse.getCookie() ) )
{
Preconditions.checkState( future == requestedCookies.poll(), "requestedCookies queue mismatch" );
} else
{
future = null; // leave for handling by backend
}
}
}
if ( future != null )
{
future.getFuture().complete( cookieResponse.getData() );
throw CancelSendSignal.INSTANCE;
}
}
@Override
public void disconnect(String reason)
{
@ -679,6 +773,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()
{
@ -775,4 +922,26 @@ public class InitialHandler extends PacketHandler implements PendingConnection
brandMessage = input;
}
}
@Override
public CompletableFuture<byte[]> retrieveCookie(String cookie)
{
Preconditions.checkState( getVersion() >= ProtocolConstants.MINECRAFT_1_20_5, "Cookies are only supported in 1.20.5 and above" );
Preconditions.checkState( loginRequest != null, "Cannot retrieve cookies for status or legacy connections" );
if ( cookie.indexOf( ':' ) == -1 )
{
// if we request an invalid resource location (no prefix) the client will respond with "minecraft:" prefix
cookie = "minecraft:" + cookie;
}
CompletableFuture<byte[]> future = new CompletableFuture<>();
synchronized ( requestedCookies )
{
requestedCookies.add( new CookieFuture( cookie, future ) );
}
unsafe.sendPacket( new CookieRequest( cookie ) );
return future;
}
}

View File

@ -6,7 +6,6 @@ import com.mojang.brigadier.suggestion.Suggestion;
import com.mojang.brigadier.suggestion.Suggestions;
import io.netty.channel.Channel;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.UUID;
import net.md_5.bungee.BungeeCord;
@ -21,6 +20,7 @@ import net.md_5.bungee.api.event.PlayerDisconnectEvent;
import net.md_5.bungee.api.event.PluginMessageEvent;
import net.md_5.bungee.api.event.SettingsChangedEvent;
import net.md_5.bungee.api.event.TabCompleteEvent;
import net.md_5.bungee.api.event.TabCompleteRequestEvent;
import net.md_5.bungee.entitymap.EntityMap;
import net.md_5.bungee.forge.ForgeConstants;
import net.md_5.bungee.netty.ChannelWrapper;
@ -32,6 +32,7 @@ import net.md_5.bungee.protocol.packet.Chat;
import net.md_5.bungee.protocol.packet.ClientChat;
import net.md_5.bungee.protocol.packet.ClientCommand;
import net.md_5.bungee.protocol.packet.ClientSettings;
import net.md_5.bungee.protocol.packet.CookieResponse;
import net.md_5.bungee.protocol.packet.FinishConfiguration;
import net.md_5.bungee.protocol.packet.KeepAlive;
import net.md_5.bungee.protocol.packet.LoginAcknowledged;
@ -128,7 +129,7 @@ public class UpstreamBridge extends PacketHandler
@Override
public boolean shouldHandle(PacketWrapper packet) throws Exception
{
return con.getServer() != null || packet.packet instanceof PluginMessage;
return con.getServer() != null || packet.packet instanceof PluginMessage || packet.packet instanceof CookieResponse;
}
@Override
@ -234,32 +235,42 @@ public class UpstreamBridge extends PacketHandler
TabCompleteEvent tabCompleteEvent = new TabCompleteEvent( con, con.getServer(), tabComplete.getCursor(), suggestions );
bungee.getPluginManager().callEvent( tabCompleteEvent );
if ( tabCompleteEvent.isCancelled() )
List<String> legacyResults = tabCompleteEvent.getSuggestions();
int start = tabComplete.getCursor().lastIndexOf( ' ' ) + 1;
int end = tabComplete.getCursor().length();
StringRange lastArgumentRange = StringRange.between( start, end );
List<Suggestion> brigadier = new ArrayList<>( legacyResults.size() );
for ( String s : legacyResults )
{
brigadier.add( new Suggestion( lastArgumentRange, s ) );
}
TabCompleteRequestEvent tabCompleteRequestEvent = new TabCompleteRequestEvent( con, con.getServer(), tabComplete.getCursor(), lastArgumentRange, new Suggestions( lastArgumentRange, brigadier ) );
tabCompleteRequestEvent.setCancelled( tabCompleteEvent.isCancelled() );
bungee.getPluginManager().callEvent( tabCompleteRequestEvent );
if ( tabCompleteRequestEvent.isCancelled() )
{
throw CancelSendSignal.INSTANCE;
}
List<String> results = tabCompleteEvent.getSuggestions();
if ( !results.isEmpty() )
Suggestions brigadierResults = tabCompleteRequestEvent.getSuggestions();
if ( !brigadierResults.isEmpty() )
{
// Unclear how to handle 1.13 commands at this point. Because we don't inject into the command packets we are unlikely to get this far unless
// Bungee plugins are adding results for commands they don't own anyway
if ( con.getPendingConnection().getVersion() < ProtocolConstants.MINECRAFT_1_13 )
{
List<String> results = new ArrayList<>( brigadierResults.getList().size() );
for ( Suggestion s : brigadierResults.getList() )
{
results.add( s.getText() );
}
con.unsafe().sendPacket( new TabCompleteResponse( results ) );
} else
{
int start = tabComplete.getCursor().lastIndexOf( ' ' ) + 1;
int end = tabComplete.getCursor().length();
StringRange range = StringRange.between( start, end );
List<Suggestion> brigadier = new LinkedList<>();
for ( String s : results )
{
brigadier.add( new Suggestion( range, s ) );
}
con.unsafe().sendPacket( new TabCompleteResponse( tabComplete.getTransactionId(), new Suggestions( range, brigadier ) ) );
con.unsafe().sendPacket( new TabCompleteResponse( tabComplete.getTransactionId(), brigadierResults ) );
}
throw CancelSendSignal.INSTANCE;
}
@ -363,6 +374,12 @@ public class UpstreamBridge extends PacketHandler
con.sendQueuedPackets();
}
@Override
public void handle(CookieResponse cookieResponse) throws Exception
{
con.getPendingConnection().handle( cookieResponse );
}
@Override
public String toString()
{

View File

@ -86,6 +86,8 @@ public abstract class EntityMap
return EntityMap_1_16_2.INSTANCE_1_20_2;
case ProtocolConstants.MINECRAFT_1_20_3:
return EntityMap_1_16_2.INSTANCE_1_20_3;
case ProtocolConstants.MINECRAFT_1_20_5:
return EntityMap_1_16_2.INSTANCE_1_20_5;
}
throw new RuntimeException( "Version " + version + " has no entity map" );
}

View File

@ -22,6 +22,7 @@ class EntityMap_1_16_2 extends EntityMap
static final EntityMap_1_16_2 INSTANCE_1_19_4 = new EntityMap_1_16_2( 0x03, 0x30 );
static final EntityMap_1_16_2 INSTANCE_1_20_2 = new EntityMap_1_16_2( -1, 0x33 );
static final EntityMap_1_16_2 INSTANCE_1_20_3 = new EntityMap_1_16_2( -1, 0x34 );
static final EntityMap_1_16_2 INSTANCE_1_20_5 = new EntityMap_1_16_2( -1, 0x36 );
//
private final int spawnPlayerId;
private final int spectateId;

View File

@ -1,34 +0,0 @@
package net.md_5.bungee.module;
import com.google.common.io.ByteStreams;
import com.google.common.io.Files;
import java.io.IOException;
import java.net.URL;
import java.net.URLConnection;
import lombok.Data;
import net.md_5.bungee.Util;
@Data
public class JenkinsModuleSource implements ModuleSource
{
@Override
public void retrieve(ModuleSpec module, ModuleVersion version)
{
System.out.println( "Attempting to Jenkins download module " + module.getName() + " v" + version.getBuild() );
try
{
URL website = new URL( "https://ci.md-5.net/job/BungeeCord/" + version.getBuild() + "/artifact/module/" + module.getName().replace( '_', '-' ) + "/target/" + module.getName() + ".jar" );
URLConnection con = website.openConnection();
// 15 second timeout at various stages
con.setConnectTimeout( 15000 );
con.setReadTimeout( 15000 );
Files.write( ByteStreams.toByteArray( con.getInputStream() ), module.getFile() );
System.out.println( "Download complete" );
} catch ( IOException ex )
{
System.out.println( "Failed to download: " + Util.exception( ex ) );
}
}
}

View File

@ -1,155 +0,0 @@
package net.md_5.bungee.module;
import com.google.common.base.Preconditions;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.InputStream;
import java.net.URI;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.logging.Level;
import net.md_5.bungee.api.ProxyServer;
import net.md_5.bungee.api.plugin.PluginDescription;
import net.md_5.bungee.util.CaseInsensitiveMap;
import org.yaml.snakeyaml.DumperOptions;
import org.yaml.snakeyaml.Yaml;
public class ModuleManager
{
private final Map<String, ModuleSource> knownSources = new HashMap<>();
public ModuleManager()
{
knownSources.put( "jenkins", new JenkinsModuleSource() );
}
// CHECKSTYLE:OFF
@SuppressFBWarnings(
{
"SF_SWITCH_FALLTHROUGH", "SF_SWITCH_NO_DEFAULT"
})
// CHECKSTYLE:ON
public void load(ProxyServer proxy, File moduleDirectory) throws Exception
{
moduleDirectory.mkdir();
ModuleVersion bungeeVersion = ModuleVersion.parse( proxy.getVersion() );
if ( bungeeVersion == null )
{
System.out.println( "Couldn't detect bungee version. Custom build?" );
return;
}
List<ModuleSpec> modules = new ArrayList<>();
File configFile = new File( "modules.yml" );
// Start Yaml
DumperOptions options = new DumperOptions();
options.setDefaultFlowStyle( DumperOptions.FlowStyle.BLOCK );
Yaml yaml = new Yaml( options );
Map<String, Object> config;
configFile.createNewFile();
try ( InputStream is = new FileInputStream( configFile ) )
{
config = (Map) yaml.load( is );
}
if ( config == null )
{
config = new CaseInsensitiveMap<>();
} else
{
config = new CaseInsensitiveMap<>( config );
}
// End yaml
List<String> defaults = new ArrayList<>();
Object readModules = config.get( "modules" );
if ( readModules != null )
{
defaults.addAll( (Collection) readModules );
}
int version = ( config.containsKey( "version" ) ) ? (int) config.get( "version" ) : 0;
switch ( version )
{
case 0:
defaults.add( "jenkins://cmd_alert" );
defaults.add( "jenkins://cmd_find" );
defaults.add( "jenkins://cmd_list" );
defaults.add( "jenkins://cmd_send" );
defaults.add( "jenkins://cmd_server" );
case 1:
defaults.add( "jenkins://reconnect_yaml" );
}
config.put( "modules", defaults );
config.put( "version", 2 );
try ( FileWriter wr = new FileWriter( configFile ) )
{
yaml.dump( config, wr );
}
for ( String s : (List<String>) config.get( "modules" ) )
{
URI uri = new URI( s );
ModuleSource source = knownSources.get( uri.getScheme() );
if ( source == null )
{
System.out.println( "Unknown module source: " + s );
continue;
}
String name = uri.getAuthority();
if ( name == null )
{
System.out.println( "Unknown module host: " + s );
continue;
}
ModuleSpec spec = new ModuleSpec( name, new File( moduleDirectory, name + ".jar" ), source );
modules.add( spec );
System.out.println( "Discovered module: " + spec );
}
for ( ModuleSpec module : modules )
{
ModuleVersion moduleVersion = ( module.getFile().exists() ) ? getVersion( module.getFile() ) : null;
if ( !bungeeVersion.equals( moduleVersion ) )
{
System.out.println( "Attempting to update plugin from " + moduleVersion + " to " + bungeeVersion );
module.getProvider().retrieve( module, bungeeVersion );
}
}
}
@SuppressFBWarnings("REC_CATCH_EXCEPTION")
private ModuleVersion getVersion(File file)
{
try ( JarFile jar = new JarFile( file ) )
{
JarEntry pdf = jar.getJarEntry( "plugin.yml" );
Preconditions.checkNotNull( pdf, "Plugin must have a plugin.yml" );
try ( InputStream in = jar.getInputStream( pdf ) )
{
PluginDescription desc = new Yaml().loadAs( in, PluginDescription.class );
return ModuleVersion.parse( desc.getVersion() );
}
} catch ( Exception ex )
{
ProxyServer.getInstance().getLogger().log( Level.WARNING, "Could not check module from file " + file, ex );
}
return null;
}
}

View File

@ -1,7 +0,0 @@
package net.md_5.bungee.module;
interface ModuleSource
{
void retrieve(ModuleSpec module, ModuleVersion version);
}

View File

@ -1,13 +0,0 @@
package net.md_5.bungee.module;
import java.io.File;
import lombok.Data;
@Data
public class ModuleSpec
{
private final String name;
private final File file;
private final ModuleSource provider;
}

View File

@ -1,35 +0,0 @@
package net.md_5.bungee.module;
import lombok.AccessLevel;
import lombok.Data;
import lombok.RequiredArgsConstructor;
@Data
@RequiredArgsConstructor(access = AccessLevel.PRIVATE)
public class ModuleVersion
{
private final String build;
private final String git;
public static ModuleVersion parse(String version)
{
int lastColon = version.lastIndexOf( ':' );
int secondLastColon = version.lastIndexOf( ':', lastColon - 1 );
if ( lastColon == -1 || secondLastColon == -1 )
{
return null;
}
String buildNumber = version.substring( lastColon + 1, version.length() );
String gitCommit = version.substring( secondLastColon + 1, lastColon ).replaceAll( "\"", "" );
if ( "unknown".equals( buildNumber ) || "unknown".equals( gitCommit ) )
{
return null;
}
return new ModuleVersion( buildNumber, gitCommit );
}
}

View File

@ -40,3 +40,4 @@ command_perms_permission=\u00a79- {0}
command_ip=\u00a79IP of {0} is {1}
illegal_chat_characters=\u00a7cIllegal characters in chat ({0})
kick_message=\u00a7cYou have been kicked off the proxy.
reject_transfer=\u00a7cYour transfer was rejected.

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.20-R0.2-SNAPSHOT</version>
<version>1.20-R0.3-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<groupId>net.md-5</groupId>
<artifactId>bungeecord-query</artifactId>
<version>1.20-R0.2-SNAPSHOT</version>
<version>1.20-R0.3-SNAPSHOT</version>
<packaging>jar</packaging>
<name>BungeeCord-Query</name>
@ -25,7 +24,7 @@
<scope>compile</scope>
</dependency>
<dependency>
<groupId>net.md-5</groupId>
<groupId>fr.pandacube.bungeecord</groupId>
<artifactId>bungeecord-api</artifactId>
<version>${project.version}</version>
<scope>compile</scope>

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.20-R0.2-SNAPSHOT</version>
<version>1.20-R0.3-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<groupId>net.md-5</groupId>
<artifactId>bungeecord-slf4j</artifactId>
<version>1.20-R0.2-SNAPSHOT</version>
<version>1.20-R0.3-SNAPSHOT</version>
<packaging>jar</packaging>
<name>BungeeCord-SLF4J</name>