Skip to content
Draft
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
import java.util.regex.Matcher;
import net.minecraft.client.Minecraft;
Expand Down Expand Up @@ -70,7 +71,7 @@ private static void calculate(RunPhase phase) {
case END -> CompletableFuture.runAsync(() -> {
TrackedRun thisRun = currentRun;
if (thisRun != null) {
Object2ObjectOpenHashMap<String, SecretData> secretsFound = new Object2ObjectOpenHashMap<>();
ConcurrentHashMap<String, SecretData> secretsFound = new ConcurrentHashMap<>();

//Update secret counts
for (Entry<String, SecretData> entry : thisRun.playersSecretData().entrySet()) {
Expand Down Expand Up @@ -148,11 +149,12 @@ private static int getSecretCountFromAchievements(JsonObject playerJson) {
}

/**
* This will either reflect the value at the start or the end depending on when this is called
* This will either reflect the value at the start or the end depending on when this is called.
* Uses ConcurrentHashMap to ensure thread-safe access from multiple async tasks.
*/
private record TrackedRun(Object2ObjectOpenHashMap<String, SecretData> playersSecretData) {
private record TrackedRun(ConcurrentHashMap<String, SecretData> playersSecretData) {
private TrackedRun() {
this(new Object2ObjectOpenHashMap<>());
this(new ConcurrentHashMap<>());
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicReferenceArray;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

Expand All @@ -43,7 +44,7 @@ public class BackpackPreview {
private static final Pattern ECHEST_PATTERN = Pattern.compile("Ender Chest.*\\((\\d+)/\\d+\\)");
private static final Pattern BACKPACK_PATTERN = Pattern.compile("Backpack.*\\(Slot #(\\d+)\\)");
private static final int STORAGE_SIZE = 27;
private static final Storage[] storages = new Storage[STORAGE_SIZE];
private static final AtomicReferenceArray<Storage> storages = new AtomicReferenceArray<>(STORAGE_SIZE);

/**
* The profile id of the currently loaded backpack preview.
Expand Down Expand Up @@ -88,7 +89,7 @@ public static void tick() {

private static void loadStorages() {
for (int index = 0; index < STORAGE_SIZE; ++index) {
storages[index] = null;
storages.set(index, null);
//Copy variable since lambdas do not like when you use iteration variables (JDK-8300691)
int index2 = index;

Expand All @@ -104,7 +105,7 @@ private static void loadStorages() {
}

return null;
}, Executors.newVirtualThreadPerTaskExecutor()).thenAcceptAsync(storage -> storages[index2] = storage, Minecraft.getInstance());
}, Executors.newVirtualThreadPerTaskExecutor()).thenAcceptAsync(storage -> storages.set(index2, storage), Minecraft.getInstance());
}
}

Expand All @@ -114,15 +115,16 @@ private static RegistryOps<Tag> getOps() {

private static void saveStorages() {
for (int index = 0; index < STORAGE_SIZE; ++index) {
if (storages[index] != null && storages[index].dirty) {
Storage storage = storages.get(index);
if (storage != null && storage.dirty) {
saveStorage(index);
}
}
}

private static void saveStorage(int index) {
//Store desired storage in a variable to ensure that the instance cannot change during async execution
Storage storage = storages[index];
Storage storage = storages.get(index);

CompletableFuture.runAsync(() -> {
Path storageFile = saveDir.resolve(index + ".nbt");
Expand All @@ -138,7 +140,7 @@ private static void updateStorage(AbstractContainerScreen<?> handledScreen) {
String title = handledScreen.getTitle().getString();
int index = getStorageIndexFromTitle(title);
if (index != -1) {
storages[index] = new Storage(handledScreen.getMenu().slots.getFirst().container, title, true);
storages.set(index, new Storage(handledScreen.getMenu().slots.getFirst().container, title, true));
}
}

Expand All @@ -147,8 +149,9 @@ public static boolean renderPreview(GuiGraphics context, Screen screen, int inde
else if (index >= 27 && index < 45) index -= 18;
else return false;

if (storages[index] == null) return false;
int rows = (storages[index].size() - 9) / 9;
Storage storage = storages.get(index);
if (storage == null) return false;
int rows = (storage.size() - 9) / 9;

int x = mouseX + 184 >= screen.width ? mouseX - 188 : mouseX + 8;
int y = Math.max(0, mouseY - 16);
Expand All @@ -157,10 +160,10 @@ public static boolean renderPreview(GuiGraphics context, Screen screen, int inde
context.blit(RenderPipelines.GUI_TEXTURED, TEXTURE, x, y + rows * 18 + 17, 0, 215, 176, 7, 256, 256);

Font textRenderer = Minecraft.getInstance().font;
context.drawString(textRenderer, storages[index].name(), x + 8, y + 6, 0xFF404040, false);
context.drawString(textRenderer, storage.name(), x + 8, y + 6, 0xFF404040, false);

for (int i = 9; i < storages[index].size(); ++i) {
ItemStack currentStack = storages[index].getStack(i);
for (int i = 9; i < storage.size(); ++i) {
ItemStack currentStack = storage.getStack(i);
int itemX = x + (i - 9) % 9 * 18 + 8;
int itemY = y + (i - 9) / 9 * 18 + 18;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,11 +57,11 @@ private record AfterImportTask(Runnable runnable, boolean async) {}
/**
* Consumers must check this field when accessing `items` and `itemsMap`, or else thread safety is not guaranteed.
*/
private static boolean itemsImported = false;
private static volatile boolean itemsImported = false;
/**
* Consumers must check this field when accessing `recipes`, or else thread safety is not guaranteed.
*/
private static boolean filesImported = false;
private static volatile boolean filesImported = false;

@Init
public static void init() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,16 +61,16 @@ public class ProfileViewerScreen extends Screen {
private static Map<String, List<Collection>> collections = Map.of();

private String playerName;
private JsonObject hypixelProfile;
private JsonObject playerProfile;
private boolean profileNotFound = false;
private String errorMessage = "No Profile";
private volatile JsonObject hypixelProfile;
private volatile JsonObject playerProfile;
private volatile boolean profileNotFound = false;
private volatile String errorMessage = "No Profile";

private int activePage = 0;
private static final String[] PAGE_NAMES = {"Skills", "Slayers", "Dungeons", "Inventories", "Collections"};
private final ProfileViewerPage[] profileViewerPages = new ProfileViewerPage[PAGE_NAMES.length];
private final List<ProfileViewerNavButton> profileViewerNavButtons = new ArrayList<>();
private RemotePlayer entity;
private volatile RemotePlayer entity;
private ProfileViewerTextWidget textWidget;

public ProfileViewerScreen(String username) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@
import java.text.DecimalFormat;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicReference;

/**
* Manages the discord rich presence. Automatically connects to discord and displays a customizable activity when playing Skyblock.
Expand All @@ -24,15 +27,15 @@ public class DiscordRPCManager {
/**
* The update task used to avoid multiple update tasks running simultaneously.
*/
public static CompletableFuture<Void> updateTask;
public static long startTimeStamp;
public static int cycleCount;
private static final AtomicReference<CompletableFuture<Void>> updateTask = new AtomicReference<>();
private static final AtomicLong startTimeStamp = new AtomicLong();
private static final AtomicInteger cycleCount = new AtomicInteger();

@Init
public static void init() {
SkyblockEvents.LEAVE.register(DiscordRPCManager::initAndUpdatePresence);
SkyblockEvents.JOIN.register(() -> {
startTimeStamp = System.currentTimeMillis();
startTimeStamp.set(System.currentTimeMillis());
initAndUpdatePresence(true);
});
}
Expand All @@ -45,7 +48,7 @@ public static void updateDataAndPresence() {
if (SkyblockerConfigManager.get().misc.richPresence.customMessage.isEmpty()) {
SkyblockerConfigManager.update(config -> config.misc.richPresence.customMessage = "Playing Skyblock");
}
if (SkyblockerConfigManager.get().misc.richPresence.cycleMode) cycleCount = (cycleCount + 1) % 3;
if (SkyblockerConfigManager.get().misc.richPresence.cycleMode) cycleCount.updateAndGet(count -> (count + 1) % 3);
initAndUpdatePresence();
}

Expand Down Expand Up @@ -73,8 +76,9 @@ private static void initAndUpdatePresence() {
* if {@link MiscConfig.RichPresence#enableRichPresence rich presence is disabled}.
*/
private static void initAndUpdatePresence(boolean initialization) {
if (updateTask == null || updateTask.isDone()) {
updateTask = CompletableFuture.runAsync(() -> {
CompletableFuture<Void> currentTask = updateTask.get();
if (currentTask == null || currentTask.isDone()) {
CompletableFuture<Void> newTask = CompletableFuture.runAsync(() -> {
if (SkyblockerConfigManager.get().misc.richPresence.enableRichPresence && Utils.isOnSkyblock()) {
if (!DiscordIPC.isConnected()) {
if (DiscordIPC.start(934607927837356052L, null)) {
Expand All @@ -94,13 +98,14 @@ private static void initAndUpdatePresence(boolean initialization) {
LOGGER.info("[Skyblocker] Discord RPC is currently disabled, will not connect");
}
}, Executors.newVirtualThreadPerTaskExecutor());
updateTask.set(newTask);
}
}

public static RichPresence buildPresence() {
RichPresence presence = new RichPresence();
presence.setLargeImage("skyblocker-default", null);
presence.setStart(startTimeStamp);
presence.setStart(startTimeStamp.get());
presence.setDetails(SkyblockerConfigManager.get().misc.richPresence.customMessage);
presence.setState(getInfo());
return presence;
Expand All @@ -115,7 +120,7 @@ public static String getInfo() {
case LOCATION -> info = Utils.getIslandArea();
}
} else if (SkyblockerConfigManager.get().misc.richPresence.cycleMode) {
switch (cycleCount) {
switch (cycleCount.get()) {
case 0 -> info = "Bits: " + DECIMAL_FORMAT.format(Utils.getBits());
case 1 -> info = "Purse: " + DECIMAL_FORMAT.format(Utils.getPurse());
case 2 -> info = Utils.getIslandArea();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,12 @@ private static void sendInternal(String message) {
try {
if (Debug.debugEnabled() && Debug.webSocketDebug()) LOGGER.info("[Skyblocker WebSocket] Sending Message: {}", message);

socket.sendText(message, true).join();
WebSocket currentSocket = socket;
if (currentSocket != null && !currentSocket.isOutputClosed()) {
currentSocket.sendText(message, true).join();
} else {
LOGGER.warn("[Skyblocker WebSocket] Cannot send message - socket is null or closed");
}
} catch (Exception e) {
LOGGER.error("[Skyblocker WebSocket] Failed to send message!", e);
}
Expand Down