diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 00000000..a856063b --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,52 @@ +name: Build With Gradle + +on: [ push, pull_request ] + +jobs: + build: + + runs-on: ubuntu-latest + + steps: + - name: Checkout repository and submodules + uses: actions/checkout@v3 + with: + submodules: recursive + + - name: Set up JDK 16 + uses: actions/setup-java@v2 + with: + distribution: 'temurin' + java-version: '16' + cache: 'gradle' + + - name: Build with Gradle + run: ./gradlew shadowJar + + - name: Archive artifacts (Bungee) + uses: actions/upload-artifact@v2 + if: success() + with: + name: BungeeTabListPlus Bungee + path: bootstrap-bungee/build/libs/BungeeTabListPlus-*-SNAPSHOT.jar + + - name: Archive artifacts (Bukkit) + uses: actions/upload-artifact@v2 + if: success() + with: + name: BungeeTabListPlus Bukkit Bridge + path: bootstrap-bukkit/build/libs/BungeeTabListPlus_BukkitBridge-*-SNAPSHOT.jar + + - name: Archive artifacts (Fabric 1.16.3) + uses: actions/upload-artifact@v2 + if: success() + with: + name: BungeeTabListPlus Fabric 1.16.3 Bridge + path: fabric-bridge-1.16.3/build/libs/btlp-fabric-bridge-*-SNAPSHOT.jar + + - name: Archive artifacts (Fabric 1.17) + uses: actions/upload-artifact@v2 + if: success() + with: + name: BungeeTabListPlus Fabric 1.17 Bridge + path: fabric-bridge-1.17/build/libs/btlp-fabric-bridge-*-SNAPSHOT.jar \ No newline at end of file diff --git a/bridge/src/main/java/de/codecrafter47/bungeetablistplus/bridge/AbstractBridge.java b/bridge/src/main/java/de/codecrafter47/bungeetablistplus/bridge/AbstractBridge.java index 836b0901..9b538bcf 100644 --- a/bridge/src/main/java/de/codecrafter47/bungeetablistplus/bridge/AbstractBridge.java +++ b/bridge/src/main/java/de/codecrafter47/bungeetablistplus/bridge/AbstractBridge.java @@ -289,64 +289,64 @@ public void resendUnconfirmedMessages() { } public void updateData(boolean isMainThread) throws IOException { - synchronized (updateDataLock) { - Map proxyIds = new HashMap<>(); + Map proxyIds = new HashMap<>(); - for (Map.Entry e : playerPlayerConnectionInfoMap.entrySet()) { - Player player = e.getKey(); - PlayerConnectionInfo connectionInfo = e.getValue(); + for (Map.Entry e : playerPlayerConnectionInfoMap.entrySet()) { + Player player = e.getKey(); + PlayerConnectionInfo connectionInfo = e.getValue(); - if (!connectionInfo.isConnectionValid) { - continue; - } + if (!connectionInfo.isConnectionValid) { + continue; + } - proxyIds.putIfAbsent(connectionInfo.proxyIdentifier, player); + proxyIds.putIfAbsent(connectionInfo.proxyIdentifier, player); - updatePlayerData(player, connectionInfo, isMainThread); - } + updatePlayerData(player, connectionInfo, isMainThread); + } - for (Map.Entry e : proxyIds.entrySet()) { - Integer proxyIdentifier = e.getKey(); - Player player = e.getValue(); - BridgeData bridgeData = serverBridgeDataMap.get(proxyIdentifier); + ArrayList dirtyEntries = new ArrayList<>(); - if (bridgeData == null) { - continue; - } + for (Map.Entry e : proxyIds.entrySet()) { + dirtyEntries.clear(); + Integer proxyIdentifier = e.getKey(); + Player player = e.getValue(); + BridgeData bridgeData = serverBridgeDataMap.get(proxyIdentifier); - int size = 0; + if (bridgeData == null) { + continue; + } - for (CacheEntry entry : bridgeData.requestedData) { - DataKey key = entry.key; - if (requiresMainThread(key) == isMainThread) { - Object value = serverDataAccess.get(key, server); - entry.dirty = !Objects.equals(value, entry.value); + for (CacheEntry entry : bridgeData.requestedData) { + DataKey key = entry.key; + if (requiresMainThread(key) == isMainThread) { + Object value = serverDataAccess.get(key, server); + synchronized (entry) { + boolean dirty = !Objects.equals(value, entry.value); entry.value = value; - if (entry.dirty) { - size++; + if (dirty) { + dirtyEntries.add(entry); } } } + } + synchronized (updateDataLock) { ByteArrayOutputStream byteArrayOutput = new ByteArrayOutputStream(); DataOutput output = new DataOutputStream(byteArrayOutput); - output.writeByte(BridgeProtocolConstants.MESSAGE_ID_UPDATE_DATA_SERVER); output.writeInt(proxyIdentifier + serverIdentifier); output.writeInt(bridgeData.nextOutgoingMessageId++); - output.writeInt(size); - - for (CacheEntry entry : bridgeData.requestedData) { - if (entry.dirty) { - output.writeInt(entry.netId); - output.writeBoolean(entry.value == null); - if (entry.value != null) { - try { - typeAdapterRegistry.getTypeAdapter((TypeToken) entry.key.getType()).write(output, entry.value); - } catch (IOException e1) { - e1.printStackTrace(); - } + output.writeInt(dirtyEntries.size()); + + for (CacheEntry entry : dirtyEntries) { + output.writeInt(entry.netId); + output.writeBoolean(entry.value == null); + if (entry.value != null) { + try { + typeAdapterRegistry.getTypeAdapter((TypeToken) entry.key.getType()).write(output, entry.value); + } catch (IOException e1) { + e1.printStackTrace(); } } } @@ -360,46 +360,44 @@ public void updateData(boolean isMainThread) throws IOException { } private void updatePlayerData(@Nonnull Player player, @Nonnull PlayerConnectionInfo connectionInfo, boolean isMainThread) throws IOException { - synchronized (updateDataLock) { - BridgeData bridgeData = connectionInfo.playerBridgeData; + BridgeData bridgeData = connectionInfo.playerBridgeData; - if (bridgeData == null) { - return; - } + if (bridgeData == null) { + return; + } - int size = 0; + ArrayList dirtyEntries = new ArrayList<>(); - for (CacheEntry entry : bridgeData.requestedData) { - DataKey key = entry.key; - if (requiresMainThread(key) == isMainThread) { - Object value = playerDataAccess.get(key, player); - entry.dirty = !Objects.equals(value, entry.value); - entry.value = value; + for (CacheEntry entry : bridgeData.requestedData) { + DataKey key = entry.key; + if (requiresMainThread(key) == isMainThread) { + Object value = playerDataAccess.get(key, player); + boolean dirty = !Objects.equals(value, entry.value); + entry.value = value; - if (entry.dirty) { - size++; - } + if (dirty) { + dirtyEntries.add(entry); } } + } - if (size != 0) { + if (!dirtyEntries.isEmpty()) { + synchronized (updateDataLock) { ByteArrayOutputStream byteArrayOutput = new ByteArrayOutputStream(); DataOutput output = new DataOutputStream(byteArrayOutput); output.writeByte(BridgeProtocolConstants.MESSAGE_ID_UPDATE_DATA); output.writeInt(connectionInfo.connectionIdentifier); output.writeInt(bridgeData.nextOutgoingMessageId++); - output.writeInt(size); - - for (CacheEntry entry : bridgeData.requestedData) { - if (entry.dirty) { - output.writeInt(entry.netId); - output.writeBoolean(entry.value == null); - if (entry.value != null) { - try { - typeAdapterRegistry.getTypeAdapter((TypeToken) entry.key.getType()).write(output, entry.value); - } catch (IOException e1) { - e1.printStackTrace(); - } + output.writeInt(dirtyEntries.size()); + + for (CacheEntry entry : dirtyEntries) { + output.writeInt(entry.netId); + output.writeBoolean(entry.value == null); + if (entry.value != null) { + try { + typeAdapterRegistry.getTypeAdapter((TypeToken) entry.key.getType()).write(output, entry.value); + } catch (IOException e1) { + e1.printStackTrace(); } } } @@ -423,7 +421,7 @@ public void setServerDataAccess(@Nonnull DataAccess serverDataAccess) { protected abstract void sendMessage(@Nonnull Player player, @Nonnull byte[] message); protected abstract void runAsync(@Nonnull Runnable task); - + protected abstract boolean requiresMainThread(@Nonnull DataKey key); private static class PlayerConnectionInfo { @@ -446,7 +444,6 @@ private static class CacheEntry { final int netId; @Nullable Object value = null; - boolean dirty = false; CacheEntry(@Nonnull DataKey key, int netId) { this.key = key; @@ -463,7 +460,7 @@ private static class BridgeData { long lastMessageSent = 0; /*** - * + * * @param key * @param netId * @return true if a new entry has been created diff --git a/build.gradle b/build.gradle index 0e5d8723..1adf9419 100644 --- a/build.gradle +++ b/build.gradle @@ -10,7 +10,7 @@ buildscript { ext { spigotVersion = '1.11-R0.1-SNAPSHOT' - bungeeVersion = '1.19-R0.1-SNAPSHOT' + bungeeVersion = '1.20-R0.2-SNAPSHOT' spongeVersion = '7.0.0' dataApiVersion = '1.0.2-SNAPSHOT' } diff --git a/bungee/src/main/java/codecrafter47/bungeetablistplus/handler/NewTabOverlayHandler.java b/bungee/src/main/java/codecrafter47/bungeetablistplus/handler/NewTabOverlayHandler.java index eba4958b..d3e271b9 100644 --- a/bungee/src/main/java/codecrafter47/bungeetablistplus/handler/NewTabOverlayHandler.java +++ b/bungee/src/main/java/codecrafter47/bungeetablistplus/handler/NewTabOverlayHandler.java @@ -33,6 +33,7 @@ import de.codecrafter47.taboverlay.handler.*; import it.unimi.dsi.fastutil.objects.*; import lombok.*; +import net.md_5.bungee.UserConnection; import net.md_5.bungee.api.connection.ProxiedPlayer; import net.md_5.bungee.protocol.DefinedPacket; import net.md_5.bungee.protocol.packet.*; @@ -181,6 +182,11 @@ private void sendPacket(DefinedPacket packet) { logVersionMismatch = true; logger.warning("Cannot correctly update tablist for player " + player.getName() + "\nThe client and server versions do not match. Client >= 1.19.3, server < 1.19.3.\nUse ViaVersion on the spigot server for the best experience."); } + } else if (player.getPendingConnection().getVersion() >= 764) { + // Ensure that unsafe packets are not sent in the config phase + // Why bungee doesn't expose this via api beyond me... + // https://github.com/SpigotMC/BungeeCord/blob/1ef4d27dbea48a1d47501ad2be0d75e42cc2cc12/proxy/src/main/java/net/md_5/bungee/UserConnection.java#L182-L192 + ((UserConnection) player).sendPacketQueued(packet); } else { player.unsafe().sendPacket(packet); }