From c23ff9c08e7ff8944fbe4b2c2592957d49a40cce Mon Sep 17 00:00:00 2001 From: JustAlittleWolf Date: Fri, 25 Aug 2023 22:14:47 +0200 Subject: [PATCH 1/4] Add Tablist buffering Useful for servers with big/complicated tablists like mccisland (boosts fps from 150 to 300 when pressing tab for me) --- .../tr7zw/exordium/access/TablistAccess.java | 10 +++ .../dev/tr7zw/exordium/config/Config.java | 1 + .../exordium/config/ExordiumConfigScreen.java | 1 + .../dev/tr7zw/exordium/mixin/GuiMixin.java | 19 +++++ .../exordium/mixin/PlayerTabOverlayMixin.java | 82 +++++++++++++++++++ .../resources/assets/exordium/lang/en_us.json | 10 ++- .../src/main/resources/exordium.mixins.json | 3 +- 7 files changed, 124 insertions(+), 2 deletions(-) create mode 100644 Shared/src/main/java/dev/tr7zw/exordium/access/TablistAccess.java create mode 100644 Shared/src/main/java/dev/tr7zw/exordium/mixin/PlayerTabOverlayMixin.java diff --git a/Shared/src/main/java/dev/tr7zw/exordium/access/TablistAccess.java b/Shared/src/main/java/dev/tr7zw/exordium/access/TablistAccess.java new file mode 100644 index 0000000..d369222 --- /dev/null +++ b/Shared/src/main/java/dev/tr7zw/exordium/access/TablistAccess.java @@ -0,0 +1,10 @@ +package dev.tr7zw.exordium.access; + +import dev.tr7zw.exordium.util.BufferedComponent; +import net.minecraft.world.scores.Objective; +import net.minecraft.world.scores.Scoreboard; + +public interface TablistAccess { + public void updateState(Scoreboard scoreboard, Objective objective); + public BufferedComponent getBufferedComponent(); +} diff --git a/Shared/src/main/java/dev/tr7zw/exordium/config/Config.java b/Shared/src/main/java/dev/tr7zw/exordium/config/Config.java index 30936e4..200825a 100644 --- a/Shared/src/main/java/dev/tr7zw/exordium/config/Config.java +++ b/Shared/src/main/java/dev/tr7zw/exordium/config/Config.java @@ -12,6 +12,7 @@ public class Config { public ComponentSettings experienceSettings = new ComponentSettings(true, 5); public ComponentSettings healthSettings = new ComponentSettings(true, 20); public ComponentSettings scoreboardSettings = new ComponentSettings(true, 5); + public ComponentSettings tablistSettings = new ComponentSettings(true, 20); public class ComponentSettings { public boolean enabled = true; diff --git a/Shared/src/main/java/dev/tr7zw/exordium/config/ExordiumConfigScreen.java b/Shared/src/main/java/dev/tr7zw/exordium/config/ExordiumConfigScreen.java index c9b3236..e1a0397 100644 --- a/Shared/src/main/java/dev/tr7zw/exordium/config/ExordiumConfigScreen.java +++ b/Shared/src/main/java/dev/tr7zw/exordium/config/ExordiumConfigScreen.java @@ -33,6 +33,7 @@ public void initialize() { addSettings(options, config.hotbarSettings, "hotbar"); addSettings(options, config.experienceSettings, "experience"); addSettings(options, config.scoreboardSettings, "scoreboard"); + addSettings(options, config.tablistSettings, "tablist"); getOptions().addSmall(options.toArray(new OptionInstance[0])); diff --git a/Shared/src/main/java/dev/tr7zw/exordium/mixin/GuiMixin.java b/Shared/src/main/java/dev/tr7zw/exordium/mixin/GuiMixin.java index 30d8591..14c772e 100644 --- a/Shared/src/main/java/dev/tr7zw/exordium/mixin/GuiMixin.java +++ b/Shared/src/main/java/dev/tr7zw/exordium/mixin/GuiMixin.java @@ -1,5 +1,9 @@ package dev.tr7zw.exordium.mixin; +import dev.tr7zw.exordium.access.TablistAccess; +import net.minecraft.client.gui.components.PlayerTabOverlay; +import net.minecraft.world.scores.Objective; +import net.minecraft.world.scores.Scoreboard; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.injection.At; @@ -18,6 +22,8 @@ public class GuiMixin { @Shadow private ChatComponent chat; + @Shadow + private PlayerTabOverlay tabList; @WrapOperation(method = "render", at = { @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/components/ChatComponent;render(Lnet/minecraft/client/gui/GuiGraphics;III)V"), @@ -31,5 +37,18 @@ private void renderChatWrapper(ChatComponent instance, GuiGraphics guiGraphics, } bufferedComponent.renderEnd(); } + + @WrapOperation(method = "render", at = { + @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/components/PlayerTabOverlay;render(Lnet/minecraft/client/gui/GuiGraphics;ILnet/minecraft/world/scores/Scoreboard;Lnet/minecraft/world/scores/Objective;)V"), + }) + private void renderTablistWrapper(PlayerTabOverlay instance, GuiGraphics guiGraphics, int screenWidth, Scoreboard scoreboard, Objective objective2, final Operation operation) { + TablistAccess tablistAccess = (TablistAccess) tabList; + tablistAccess.updateState(scoreboard, objective2); + BufferedComponent bufferedComponent = tablistAccess.getBufferedComponent(); + if(!bufferedComponent.render()) { + operation.call(instance, guiGraphics, screenWidth, scoreboard, objective2); + } + bufferedComponent.renderEnd(); + } } diff --git a/Shared/src/main/java/dev/tr7zw/exordium/mixin/PlayerTabOverlayMixin.java b/Shared/src/main/java/dev/tr7zw/exordium/mixin/PlayerTabOverlayMixin.java new file mode 100644 index 0000000..66f2cbd --- /dev/null +++ b/Shared/src/main/java/dev/tr7zw/exordium/mixin/PlayerTabOverlayMixin.java @@ -0,0 +1,82 @@ +package dev.tr7zw.exordium.mixin; + +import dev.tr7zw.exordium.ExordiumModBase; +import dev.tr7zw.exordium.access.TablistAccess; +import dev.tr7zw.exordium.util.BufferedComponent; +import net.minecraft.client.Minecraft; +import net.minecraft.client.multiplayer.PlayerInfo; +import net.minecraft.network.chat.Component; +import net.minecraft.world.scores.Objective; +import net.minecraft.world.scores.Score; +import net.minecraft.world.scores.Scoreboard; +import org.spongepowered.asm.mixin.Mixin; +import net.minecraft.client.gui.components.PlayerTabOverlay; +import org.spongepowered.asm.mixin.Shadow; + +import java.util.List; + +@Mixin(PlayerTabOverlay.class) +public abstract class PlayerTabOverlayMixin implements TablistAccess { + @Shadow + private Minecraft minecraft; + private int playerInfoHash = 0; + private int headerHash = 0; + private int footerHash = 0; + private int scoreboardHash = 0; + private int objectiveHash = 0; + private boolean scoreboardOrObjectiveChange = false; + @Shadow + private Component header; + @Shadow + private Component footer; + private Objective lastTrackedObjective; + + private BufferedComponent bufferedComponent = new BufferedComponent(true, ExordiumModBase.instance.config.tablistSettings) { + + @Override + public boolean needsRender() { + return playerInfoHash != fastGetPlayerInfoListHashCode(getPlayerInfos()) || headerHash != header.getString().hashCode() || footerHash != footer.getString().hashCode() || scoreboardOrObjectiveChange; + } + + @Override + public void captureState() { + playerInfoHash = fastGetPlayerInfoListHashCode(getPlayerInfos()); + headerHash = header.getString().hashCode(); + footerHash = footer.getString().hashCode(); + } + }; + + public void updateState(Scoreboard scoreboard, Objective objective) { + scoreboardOrObjectiveChange = scoreboardOrObjectiveChanged(scoreboard, objective); + } + + public boolean scoreboardOrObjectiveChanged(Scoreboard scoreboard, Objective objective) { + if(objective == null && lastTrackedObjective == null) return false; + + int scoreboardHashCode = 1; + for (Score score : scoreboard.getPlayerScores(objective)) + scoreboardHashCode = 31 * scoreboardHashCode + (score == null ? 0 : score.getScore()); + boolean scoreboardChanged = scoreboardHashCode != scoreboardHash; + + int newObjectiveHashCode = objective == null ? 0 : objective.getName().hashCode(); + if(!scoreboardChanged && newObjectiveHashCode == objectiveHash) return false; + scoreboardHash = scoreboardHashCode; + objectiveHash = newObjectiveHashCode; + lastTrackedObjective = objective; + return true; + } + + public int fastGetPlayerInfoListHashCode(List playerInfos) { + int hashCode = 1; + for (PlayerInfo playerInfo : playerInfos) + hashCode = 31 * hashCode + (playerInfo == null ? 0 : playerInfo.getProfile().getId().hashCode()); + return hashCode; + } + + public BufferedComponent getBufferedComponent() { + return bufferedComponent; + } + + @Shadow + public abstract List getPlayerInfos(); +} diff --git a/Shared/src/main/resources/assets/exordium/lang/en_us.json b/Shared/src/main/resources/assets/exordium/lang/en_us.json index 624508e..ca686f1 100644 --- a/Shared/src/main/resources/assets/exordium/lang/en_us.json +++ b/Shared/src/main/resources/assets/exordium/lang/en_us.json @@ -51,5 +51,13 @@ "text.exordium.setting.scoreboard.forceblend": "Force Scoreboard Blend", "text.exordium.setting.scoreboard.forceblend.tooltip": "Force on texture blending for the scoreboard. Might fix some issues with other mods.", "text.exordium.setting.scoreboard.forceupdates": "Force Scoreboard Updates", - "text.exordium.setting.scoreboard.forceupdates.tooltip": "Force the scoreboard to update every frame(keeping in mind the fps limit). Might fix some issues with other mods." + "text.exordium.setting.scoreboard.forceupdates.tooltip": "Force the scoreboard to update every frame(keeping in mind the fps limit). Might fix some issues with other mods.", + "text.exordium.setting.tablist.enabled": "Buffer Tablist", + "text.exordium.setting.tablist.fps": "Buffer Tablist Max. FPS", + "text.exordium.setting.tablist.fps.tooltip": "The maximum framerate for the tablist", + "text.exordium.setting.tablist.enabled.tooltip": "Enable/Disable buffering the tablist", + "text.exordium.setting.tablist.forceblend": "Force Tablist Blend", + "text.exordium.setting.tablist.forceblend.tooltip": "Force on texture blending for the tablist. Might fix some issues with other mods.", + "text.exordium.setting.tablist.forceupdates": "Force Tablist Updates", + "text.exordium.setting.tablist.forceupdates.tooltip": "Force the tablist to update every frame(keeping in mind the fps limit). Might fix some issues with other mods." } \ No newline at end of file diff --git a/Shared/src/main/resources/exordium.mixins.json b/Shared/src/main/resources/exordium.mixins.json index a40d014..276166b 100644 --- a/Shared/src/main/resources/exordium.mixins.json +++ b/Shared/src/main/resources/exordium.mixins.json @@ -19,7 +19,8 @@ "GuiHealthMixin", "ScoreboardMixin", "GuiMixin", - "GameRendererMixin" + "GameRendererMixin", + "PlayerTabOverlayMixin" ], "injectors": { "defaultRequire": 1 From 287093d83f4584acafeb09e221ab90627311a0fa Mon Sep 17 00:00:00 2001 From: JustAlittleWolf Date: Fri, 25 Aug 2023 22:39:11 +0200 Subject: [PATCH 2/4] Fix crash for worlds without a header or footer --- .../tr7zw/exordium/mixin/PlayerTabOverlayMixin.java | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/Shared/src/main/java/dev/tr7zw/exordium/mixin/PlayerTabOverlayMixin.java b/Shared/src/main/java/dev/tr7zw/exordium/mixin/PlayerTabOverlayMixin.java index 66f2cbd..ab1d9a7 100644 --- a/Shared/src/main/java/dev/tr7zw/exordium/mixin/PlayerTabOverlayMixin.java +++ b/Shared/src/main/java/dev/tr7zw/exordium/mixin/PlayerTabOverlayMixin.java @@ -35,14 +35,16 @@ public abstract class PlayerTabOverlayMixin implements TablistAccess { @Override public boolean needsRender() { - return playerInfoHash != fastGetPlayerInfoListHashCode(getPlayerInfos()) || headerHash != header.getString().hashCode() || footerHash != footer.getString().hashCode() || scoreboardOrObjectiveChange; + int newHeaderHash = header == null ? 0 : header.getString().hashCode(); + int newFooterHash = footer == null ? 0 : footer.getString().hashCode(); + return playerInfoHash != fastGetPlayerInfoListHashCode(getPlayerInfos()) || headerHash != newHeaderHash || footerHash != newFooterHash || scoreboardOrObjectiveChange; } @Override public void captureState() { playerInfoHash = fastGetPlayerInfoListHashCode(getPlayerInfos()); - headerHash = header.getString().hashCode(); - footerHash = footer.getString().hashCode(); + headerHash = header == null ? 0 : header.getString().hashCode(); + footerHash = footer == null ? 0 : footer.getString().hashCode(); } }; @@ -56,10 +58,9 @@ public boolean scoreboardOrObjectiveChanged(Scoreboard scoreboard, Objective obj int scoreboardHashCode = 1; for (Score score : scoreboard.getPlayerScores(objective)) scoreboardHashCode = 31 * scoreboardHashCode + (score == null ? 0 : score.getScore()); - boolean scoreboardChanged = scoreboardHashCode != scoreboardHash; int newObjectiveHashCode = objective == null ? 0 : objective.getName().hashCode(); - if(!scoreboardChanged && newObjectiveHashCode == objectiveHash) return false; + if(scoreboardHashCode == scoreboardHash && newObjectiveHashCode == objectiveHash) return false; scoreboardHash = scoreboardHashCode; objectiveHash = newObjectiveHashCode; lastTrackedObjective = objective; From cf4d27f84326cc0f5a3499fd8a3beffc29be329d Mon Sep 17 00:00:00 2001 From: JustAlittleWolf Date: Fri, 25 Aug 2023 23:01:24 +0200 Subject: [PATCH 3/4] Update tablist on displayname change --- .../java/dev/tr7zw/exordium/mixin/PlayerTabOverlayMixin.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Shared/src/main/java/dev/tr7zw/exordium/mixin/PlayerTabOverlayMixin.java b/Shared/src/main/java/dev/tr7zw/exordium/mixin/PlayerTabOverlayMixin.java index ab1d9a7..107d612 100644 --- a/Shared/src/main/java/dev/tr7zw/exordium/mixin/PlayerTabOverlayMixin.java +++ b/Shared/src/main/java/dev/tr7zw/exordium/mixin/PlayerTabOverlayMixin.java @@ -70,7 +70,7 @@ public boolean scoreboardOrObjectiveChanged(Scoreboard scoreboard, Objective obj public int fastGetPlayerInfoListHashCode(List playerInfos) { int hashCode = 1; for (PlayerInfo playerInfo : playerInfos) - hashCode = 31 * hashCode + (playerInfo == null ? 0 : playerInfo.getProfile().getId().hashCode()); + hashCode = 31 * hashCode + (playerInfo == null ? 0 : (playerInfo.getTabListDisplayName() == null ? playerInfo.getProfile().getId().hashCode() : playerInfo.getTabListDisplayName().getString().hashCode())); return hashCode; } From 35c8bbd3786aa52c7b2af98812e6edc001a50dc0 Mon Sep 17 00:00:00 2001 From: JustAlittleWolf Date: Fri, 25 Aug 2023 23:23:48 +0200 Subject: [PATCH 4/4] update on player skin resourcelocation change for some reason it displays the default skin otherwise. also moved some stuff from needsRender() to updateState() where it belongs --- .../exordium/mixin/PlayerTabOverlayMixin.java | 29 ++++++++++++------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/Shared/src/main/java/dev/tr7zw/exordium/mixin/PlayerTabOverlayMixin.java b/Shared/src/main/java/dev/tr7zw/exordium/mixin/PlayerTabOverlayMixin.java index 107d612..62f7ee1 100644 --- a/Shared/src/main/java/dev/tr7zw/exordium/mixin/PlayerTabOverlayMixin.java +++ b/Shared/src/main/java/dev/tr7zw/exordium/mixin/PlayerTabOverlayMixin.java @@ -24,20 +24,17 @@ public abstract class PlayerTabOverlayMixin implements TablistAccess { private int footerHash = 0; private int scoreboardHash = 0; private int objectiveHash = 0; - private boolean scoreboardOrObjectiveChange = false; @Shadow private Component header; @Shadow private Component footer; private Objective lastTrackedObjective; - + private boolean outdated; private BufferedComponent bufferedComponent = new BufferedComponent(true, ExordiumModBase.instance.config.tablistSettings) { @Override public boolean needsRender() { - int newHeaderHash = header == null ? 0 : header.getString().hashCode(); - int newFooterHash = footer == null ? 0 : footer.getString().hashCode(); - return playerInfoHash != fastGetPlayerInfoListHashCode(getPlayerInfos()) || headerHash != newHeaderHash || footerHash != newFooterHash || scoreboardOrObjectiveChange; + return outdated; } @Override @@ -49,18 +46,21 @@ public void captureState() { }; public void updateState(Scoreboard scoreboard, Objective objective) { - scoreboardOrObjectiveChange = scoreboardOrObjectiveChanged(scoreboard, objective); + boolean scoreboardOrObjectiveChange = scoreboardOrObjectiveChanged(scoreboard, objective); + int newHeaderHash = header == null ? 0 : header.getString().hashCode(); + int newFooterHash = footer == null ? 0 : footer.getString().hashCode(); + outdated = playerInfoHash != fastGetPlayerInfoListHashCode(getPlayerInfos()) || headerHash != newHeaderHash || footerHash != newFooterHash || scoreboardOrObjectiveChange; } public boolean scoreboardOrObjectiveChanged(Scoreboard scoreboard, Objective objective) { - if(objective == null && lastTrackedObjective == null) return false; + if (objective == null && lastTrackedObjective == null) return false; int scoreboardHashCode = 1; for (Score score : scoreboard.getPlayerScores(objective)) scoreboardHashCode = 31 * scoreboardHashCode + (score == null ? 0 : score.getScore()); int newObjectiveHashCode = objective == null ? 0 : objective.getName().hashCode(); - if(scoreboardHashCode == scoreboardHash && newObjectiveHashCode == objectiveHash) return false; + if (scoreboardHashCode == scoreboardHash && newObjectiveHashCode == objectiveHash) return false; scoreboardHash = scoreboardHashCode; objectiveHash = newObjectiveHashCode; lastTrackedObjective = objective; @@ -69,8 +69,17 @@ public boolean scoreboardOrObjectiveChanged(Scoreboard scoreboard, Objective obj public int fastGetPlayerInfoListHashCode(List playerInfos) { int hashCode = 1; - for (PlayerInfo playerInfo : playerInfos) - hashCode = 31 * hashCode + (playerInfo == null ? 0 : (playerInfo.getTabListDisplayName() == null ? playerInfo.getProfile().getId().hashCode() : playerInfo.getTabListDisplayName().getString().hashCode())); + for (PlayerInfo playerInfo : playerInfos) { + int combinedHashes = 0; + if (playerInfo == null) { + hashCode *= 31; + continue; + } + combinedHashes += playerInfo.getProfile().getId().hashCode(); + combinedHashes += playerInfo.getTabListDisplayName() == null ? 0 : playerInfo.getTabListDisplayName().getString().hashCode(); + combinedHashes += playerInfo.getSkinLocation().hashCode(); + hashCode = 31 * hashCode + combinedHashes; + } return hashCode; }