Skip to content

Commit

Permalink
Migrate PlayerList
Browse files Browse the repository at this point in the history
  • Loading branch information
tr7zw committed Aug 3, 2024
1 parent af4d023 commit 24d3b63
Show file tree
Hide file tree
Showing 5 changed files with 155 additions and 126 deletions.
23 changes: 19 additions & 4 deletions src/main/java/dev/tr7zw/exordium/access/TablistAccess.java
Original file line number Diff line number Diff line change
@@ -1,9 +1,24 @@
package dev.tr7zw.exordium.access;

import net.minecraft.world.scores.Objective;
import net.minecraft.world.scores.Scoreboard;
import java.util.List;
import java.util.Map;
import java.util.UUID;

public interface TablistAccess extends VanillaBufferAccess.PlayerListOverlayAccess {
void updateState(Scoreboard scoreboard, Objective objective);
import net.minecraft.client.gui.Gui;
import net.minecraft.client.gui.components.PlayerTabOverlay;
import net.minecraft.client.multiplayer.PlayerInfo;
import net.minecraft.network.chat.Component;

public interface TablistAccess {

Gui getGui();

Map<UUID, PlayerTabOverlay.HealthState> getHealthStates();

Component getHeader();

Component getFooter();

List<PlayerInfo> getPlayerInfos();

}
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,6 @@

public interface VanillaBufferAccess {

interface PlayerListOverlayAccess extends VanillaBufferAccess {

LegacyBuffer getPlayerListOverlayBuffer();
}

interface BossHealthOverlayAccess extends VanillaBufferAccess {
LegacyBuffer getHotbarOverlayBuffer();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
package dev.tr7zw.exordium.components.vanilla;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

import dev.tr7zw.exordium.access.TablistAccess;
import dev.tr7zw.exordium.components.BufferComponent;
import dev.tr7zw.util.NMSHelper;
import lombok.Getter;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.components.PlayerTabOverlay;
import net.minecraft.client.multiplayer.PlayerInfo;
import net.minecraft.network.chat.Component;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.level.GameType;
import net.minecraft.world.scores.Objective;
import net.minecraft.world.scores.PlayerScoreEntry;
import net.minecraft.world.scores.PlayerTeam;
import net.minecraft.world.scores.Scoreboard;
import net.minecraft.world.scores.criteria.ObjectiveCriteria;

public class PlayerListComponent
implements BufferComponent<dev.tr7zw.exordium.components.vanilla.PlayerListComponent.PlayerListContext> {

private static final Minecraft minecraft = Minecraft.getInstance();
@Getter
private static final ResourceLocation id = NMSHelper.getResourceLocation("minecraft", "crosshair");

private List<Integer> playerInfoHashes = new ArrayList<>();
private int headerHash = 0;
private int footerHash = 0;
private int scoreboardHash = 0;
private int objectiveHash = 0;
private Objective lastTrackedObjective;

public record PlayerListContext(TablistAccess tablist, Scoreboard scoreboard, Objective objective) {
}

@Override
public void captureState(PlayerListContext context) {
playerInfoHashes = fastGetPlayerInfoListHashCode(context, context.tablist().getPlayerInfos());
headerHash = context.tablist().getHeader() == null ? 0 : context.tablist().getHeader().getString().hashCode();
footerHash = context.tablist().getFooter() == null ? 0 : context.tablist().getFooter().getString().hashCode();
}

@Override
public boolean hasChanged(int tickCount, PlayerListContext context) {
boolean scoreboardOrObjectiveChange = scoreboardOrObjectiveChanged(context.scoreboard, context.objective);
int newHeaderHash = context.tablist().getHeader() == null ? 0
: context.tablist().getHeader().getString().hashCode();
int newFooterHash = context.tablist().getFooter() == null ? 0
: context.tablist().getFooter().getString().hashCode();
boolean plaverInfoOutdated = !playerInfoHashes
.equals(fastGetPlayerInfoListHashCode(context, context.tablist().getPlayerInfos()));
return plaverInfoOutdated || headerHash != newHeaderHash || footerHash != newFooterHash
|| scoreboardOrObjectiveChange;
}

public boolean scoreboardOrObjectiveChanged(Scoreboard scoreboard, Objective objective) {
if (objective == null && lastTrackedObjective == null)
return false;

int scoreboardHashCode = 1;
for (PlayerScoreEntry score : scoreboard.listPlayerScores(objective))
scoreboardHashCode = 31 * scoreboardHashCode + (score == null ? 0 : score.value());

int newObjectiveHashCode = objective == null ? 0 : objective.getName().hashCode();
if (scoreboardHashCode == scoreboardHash && newObjectiveHashCode == objectiveHash)
return false;
scoreboardHash = scoreboardHashCode;
objectiveHash = newObjectiveHashCode;
lastTrackedObjective = objective;
return true;
}

public List<Integer> fastGetPlayerInfoListHashCode(PlayerListContext context, List<PlayerInfo> playerInfos) {
ArrayList<Integer> hashCodes = new ArrayList<>();
for (PlayerInfo playerInfo : playerInfos) {
if (playerInfo == null)
continue;

int playerHash = playerInfo.getProfile().getId().hashCode();
playerHash += playerInfo.getProfile().getName().hashCode();
if (playerInfo.getTabListDisplayName() != null) {
playerHash += playerInfo.getTabListDisplayName().getString().hashCode();
playerHash += playerInfo.getTabListDisplayName().getStyle().hashCode();
} else {
PlayerTeam playerTeam = playerInfo.getTeam();
if (playerTeam == null)
continue;
Component prefix = playerTeam.getPlayerPrefix();
Component suffix = playerTeam.getPlayerSuffix();
playerHash += Objects.hash(playerTeam.getColor(), prefix.getStyle(), prefix.getString(),
suffix.getStyle(), suffix.getString());
}
playerHash += playerInfo.getGameMode() == GameType.SPECTATOR ? 31 : 0;
playerHash += playerInfo.getSkin().texture().hashCode();
playerHash += playerInfo.getLatency() * 63;

if (lastTrackedObjective != null
&& lastTrackedObjective.getRenderType() == ObjectiveCriteria.RenderType.HEARTS) {
Player player = minecraft.level.getPlayerByUUID(playerInfo.getProfile().getId());

if (player != null) {
PlayerTabOverlay.HealthState healthState = context.tablist().getHealthStates().computeIfAbsent(
playerInfo.getProfile().getId(),
(_uuid) -> new PlayerTabOverlay.HealthState(lastTrackedObjective.getScoreboard()
.getOrCreatePlayerScore(player, lastTrackedObjective).get()));
playerHash += healthState.isBlinking(context.tablist.getGui().getGuiTicks()) ? 63 : 127;
}
}
hashCodes.add(playerHash);
}
return hashCodes;
}

}
12 changes: 7 additions & 5 deletions src/main/java/dev/tr7zw/exordium/mixin/GuiMixin.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
import dev.tr7zw.exordium.access.TablistAccess;
import dev.tr7zw.exordium.access.VanillaBufferAccess;
import dev.tr7zw.exordium.components.BufferInstance;
import dev.tr7zw.exordium.components.vanilla.PlayerListComponent;
import dev.tr7zw.exordium.components.vanilla.PlayerListComponent.PlayerListContext;
import dev.tr7zw.exordium.render.LegacyBuffer;
import net.minecraft.client.gui.Gui;
import net.minecraft.client.gui.GuiGraphics;
Expand Down Expand Up @@ -55,12 +57,13 @@ private void renderChatWrapper(ChatComponent instance, GuiGraphics guiGraphics,
private void renderTablistWrapper(PlayerTabOverlay instance, GuiGraphics guiGraphics, int screenWidth,
Scoreboard scoreboard, Objective objective2, final Operation<Void> operation) {
TablistAccess tablistAccess = (TablistAccess) tabList;
tablistAccess.updateState(scoreboard, objective2);
LegacyBuffer bufferedComponent = tablistAccess.getPlayerListOverlayBuffer();
if (!bufferedComponent.render()) {
BufferInstance<PlayerListContext> buffer = ExordiumModBase.instance.getBufferManager()
.getBufferInstance(PlayerListComponent.getId(), PlayerListContext.class);
PlayerListContext context = new PlayerListContext(tablistAccess, scoreboard, objective2);
if (!buffer.renderBuffer(tickCount, context)) {
operation.call(instance, guiGraphics, screenWidth, scoreboard, objective2);
}
bufferedComponent.renderEnd();
buffer.postRender(context);
}

@WrapOperation(method = "method_55808", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/components/BossHealthOverlay;render(Lnet/minecraft/client/gui/GuiGraphics;)V"))
Expand All @@ -69,7 +72,6 @@ private void renderBossBarWrapper(BossHealthOverlay instance, GuiGraphics guiGra
.getBossOverlay();
LegacyBuffer hotbarOverlayBuffer = overlayAccess.getHotbarOverlayBuffer();
if (!hotbarOverlayBuffer.render()) {
System.out.println("Re rendering");
original.call(instance, guiGraphics);
}
hotbarOverlayBuffer.renderEnd();
Expand Down
122 changes: 10 additions & 112 deletions src/main/java/dev/tr7zw/exordium/mixin/PlayerTabOverlayMixin.java
Original file line number Diff line number Diff line change
@@ -1,140 +1,38 @@
package dev.tr7zw.exordium.mixin;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.UUID;

import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;

import dev.tr7zw.exordium.ExordiumModBase;
import dev.tr7zw.exordium.access.TablistAccess;
import dev.tr7zw.exordium.render.BufferedComponent;
import dev.tr7zw.exordium.render.LegacyBuffer;
import net.minecraft.client.Minecraft;
import lombok.Getter;
import net.minecraft.client.gui.Gui;
import net.minecraft.client.gui.components.PlayerTabOverlay;
import net.minecraft.client.multiplayer.PlayerInfo;
import net.minecraft.network.chat.Component;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.level.GameType;
import net.minecraft.world.scores.Objective;
import net.minecraft.world.scores.PlayerScoreEntry;
import net.minecraft.world.scores.PlayerTeam;
import net.minecraft.world.scores.Scoreboard;
import net.minecraft.world.scores.criteria.ObjectiveCriteria;

@Mixin(PlayerTabOverlay.class)
public abstract class PlayerTabOverlayMixin implements TablistAccess {
@Shadow
private Minecraft minecraft;
public class PlayerTabOverlayMixin implements TablistAccess {

@Shadow
@Getter
private Gui gui;
@Shadow
@Getter
private Map<UUID, PlayerTabOverlay.HealthState> healthStates;
private ArrayList<Integer> playerInfoHashes = new ArrayList<>();
private int headerHash = 0;
private int footerHash = 0;
private int scoreboardHash = 0;
private int objectiveHash = 0;
@Shadow
@Getter
private Component header;
@Shadow
@Getter
private Component footer;
private Objective lastTrackedObjective;
private boolean outdated;
private LegacyBuffer playerlistBufferedComponent = new LegacyBuffer(true,
() -> ExordiumModBase.instance.config.tablistSettings) {

@Override
public boolean shouldRenderNextCappedFrame() {
return outdated;
}

@Override
public void captureState() {
playerInfoHashes = fastGetPlayerInfoListHashCode(getPlayerInfos());
headerHash = header == null ? 0 : header.getString().hashCode();
footerHash = footer == null ? 0 : footer.getString().hashCode();
}
};

@Override
public void updateState(Scoreboard scoreboard, Objective objective) {
boolean scoreboardOrObjectiveChange = scoreboardOrObjectiveChanged(scoreboard, objective);
int newHeaderHash = header == null ? 0 : header.getString().hashCode();
int newFooterHash = footer == null ? 0 : footer.getString().hashCode();
boolean plaverInfoOutdated = !playerInfoHashes.equals(fastGetPlayerInfoListHashCode(getPlayerInfos()));
outdated = plaverInfoOutdated || headerHash != newHeaderHash || footerHash != newFooterHash
|| scoreboardOrObjectiveChange;
}

public boolean scoreboardOrObjectiveChanged(Scoreboard scoreboard, Objective objective) {
if (objective == null && lastTrackedObjective == null)
return false;

int scoreboardHashCode = 1;
for (PlayerScoreEntry score : scoreboard.listPlayerScores(objective))
scoreboardHashCode = 31 * scoreboardHashCode + (score == null ? 0 : score.value());

int newObjectiveHashCode = objective == null ? 0 : objective.getName().hashCode();
if (scoreboardHashCode == scoreboardHash && newObjectiveHashCode == objectiveHash)
return false;
scoreboardHash = scoreboardHashCode;
objectiveHash = newObjectiveHashCode;
lastTrackedObjective = objective;
return true;
}

public ArrayList<Integer> fastGetPlayerInfoListHashCode(List<PlayerInfo> playerInfos) {
ArrayList<Integer> hashCodes = new ArrayList<>();
for (PlayerInfo playerInfo : playerInfos) {
if (playerInfo == null)
continue;

int playerHash = playerInfo.getProfile().getId().hashCode();
playerHash += playerInfo.getProfile().getName().hashCode();
if (playerInfo.getTabListDisplayName() != null) {
playerHash += playerInfo.getTabListDisplayName().getString().hashCode();
playerHash += playerInfo.getTabListDisplayName().getStyle().hashCode();
} else {
PlayerTeam playerTeam = playerInfo.getTeam();
if (playerTeam == null)
continue;
Component prefix = playerTeam.getPlayerPrefix();
Component suffix = playerTeam.getPlayerSuffix();
playerHash += Objects.hash(playerTeam.getColor(), prefix.getStyle(), prefix.getString(),
suffix.getStyle(), suffix.getString());
}
playerHash += playerInfo.getGameMode() == GameType.SPECTATOR ? 31 : 0;
playerHash += playerInfo.getSkin().texture().hashCode();
playerHash += playerInfo.getLatency() * 63;

if (lastTrackedObjective != null
&& lastTrackedObjective.getRenderType() == ObjectiveCriteria.RenderType.HEARTS) {
Player player = minecraft.level.getPlayerByUUID(playerInfo.getProfile().getId());

if (player != null) {
PlayerTabOverlay.HealthState healthState = this.healthStates.computeIfAbsent(
playerInfo.getProfile().getId(),
(_uuid) -> new PlayerTabOverlay.HealthState(lastTrackedObjective.getScoreboard()
.getOrCreatePlayerScore(player, lastTrackedObjective).get()));
playerHash += healthState.isBlinking(this.gui.getGuiTicks()) ? 63 : 127;
}
}
hashCodes.add(playerHash);
}
return hashCodes;
}

@Shadow
public abstract List<PlayerInfo> getPlayerInfos();

@Override
public LegacyBuffer getPlayerListOverlayBuffer() {
return playerlistBufferedComponent;
}
public List<PlayerInfo> getPlayerInfos() {
return null;
};

}

0 comments on commit 24d3b63

Please sign in to comment.