Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
59e51c7
Remove legacy pvp module
ArikSquad Aug 4, 2026
9939f7c
Merge commit '3617fcee05d55f52d9613d0e2146dbe7361df99c' as 'pvp'
ArikSquad Aug 4, 2026
3617fce
Squashed 'pvp/' content from commit 0ac087ffe
ArikSquad Aug 4, 2026
c941988
feat: migrate to new pvp module
ArikSquad Aug 4, 2026
1aaa738
feat(clientmod): add bedwars spray location captures
ArikSquad Aug 4, 2026
cf659f1
feat: add waiting lobby removals, config for sprays
ArikSquad Aug 4, 2026
8e65bc9
refactor: move explosion to TypeBedWarsGameLoader
ArikSquad Aug 4, 2026
9d7e581
feat: game kill tracking
ArikSquad Aug 4, 2026
0ef6ff0
feat: identity hiding properly
ArikSquad Aug 4, 2026
f4b0916
feat: scoreboard logic flipped and typo
ArikSquad Aug 4, 2026
5ce29cd
feat: generator splitting, pearls, replay fixes, scoreboard and tab o…
ArikSquad Aug 4, 2026
c395f18
feat: improve player hide logic
ArikSquad Aug 4, 2026
70b196a
fix: track attackers properly
ArikSquad Aug 4, 2026
3f59b83
feat: language selector
ArikSquad Aug 4, 2026
a8bff53
fix: viewer settings invalid back button slot
ArikSquad Aug 4, 2026
de571d0
perf: improve the replay system
ArikSquad Aug 4, 2026
5f45a5a
refactor: comment usage of PlayerChannelHandler
ArikSquad Aug 4, 2026
014eac5
feat: snap & yaw, pitch correction
ArikSquad Aug 4, 2026
0218d59
refactor: update speedway to be correct
ArikSquad Aug 4, 2026
8be568e
refactor: update lighthouse to be correct
ArikSquad Aug 4, 2026
b1432b5
feat: team metadata
ArikSquad Aug 4, 2026
0b28572
feat(replay): overhaul replay system
ArikSquad Aug 4, 2026
4ab804c
fix(bw): don't install vri
ArikSquad Aug 4, 2026
1abebce
fix(replay): actual scoreboard
ArikSquad Aug 4, 2026
7fbfd80
fix(replay): keep updating actionbar
ArikSquad Aug 4, 2026
eafa899
Squashed 'pvp/' changes from 0ac087ffe..9f9280efd
ArikSquad Aug 4, 2026
fcc0aa6
Merge commit 'eafa8990074b1e21be4b7b68593c7eb21362dcf8' into feat/bw-…
ArikSquad Aug 4, 2026
ac67702
feat(replay): use i18n
ArikSquad Aug 5, 2026
c2ffc5d
feat: show teams in tab
ArikSquad Aug 5, 2026
dc6dafc
Merge branch 'feat/show-viewer-and-teams-in-tab' into feat/bw-finalized
ArikSquad Aug 5, 2026
32e126d
feat(replay): tab
ArikSquad Aug 5, 2026
ca125e3
feat(bw): drop resources on gen after final death, closes #788
ArikSquad Aug 6, 2026
8365df1
Merge remote-tracking branch 'origin/master' into feat/bw-finalized
ArikSquad Aug 7, 2026
9cf5001
fix(bw): should always teleport to specloc
ArikSquad Aug 8, 2026
7f86add
Merge remote-tracking branch 'upstream/master' into feat/bw-finalized
ArikSquad Aug 8, 2026
09bfc57
fix(replay): import I18n
ArikSquad Aug 8, 2026
48c3f6a
fix(replay): import all the rest too
ArikSquad Aug 8, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

[<img src="https://discordapp.com/assets/e4923594e694a21542a489471ecffa50.svg" alt="Discord" height="55" />](https://discord.swofty.net)

A 26.1.2 Minestom-based recreation of Hypixel SkyBlock with a properly abstracted, scalable microservices architecture.
A 26.2 Minestom-based recreation of Hypixel with a properly abstracted, scalable microservices architecture.

> **Note**: This implementation is under active development and is not yet production-ready.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ public static void register() {
.executes(context -> start(context, ChunkExportRecorder.CaptureMode.CHUNKS))
.then(ClientCommands.literal("block_displays")
.executes(context -> start(context, ChunkExportRecorder.CaptureMode.BLOCK_DISPLAYS)))
.then(ClientCommands.literal("bedwars")
.executes(context -> start(context, ChunkExportRecorder.CaptureMode.BEDWARS)))
.then(ClientCommands.literal("ravengard")
.then(ClientCommands.argument("name", StringArgumentType.string())
.executes(context -> startStitchedRavengard(context,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ public final class KeybindRegistry {
public static KeyMapping copyLoreKey;
public static KeyMapping copyChatKey;
public static KeyMapping copyGuiKey;
public static KeyMapping copyCurrentGuiKey;
public static KeyMapping entityPacketInspectorKey;
private static boolean chatCopyPressed = false;

Expand All @@ -25,6 +26,12 @@ public static void register() {
KeyMapping.Category.MISC
));

copyCurrentGuiKey = KeyMappingHelper.registerKeyMapping(new KeyMapping(
"key.thehypixelrecreationmod.copycurrentgui",
GLFW.GLFW_KEY_PAGE_DOWN,
KeyMapping.Category.MISC
));

copyLoreKey = KeyMappingHelper.registerKeyMapping(new KeyMapping(
"key.thehypixelrecreationmod.copylore",
GLFW.GLFW_KEY_L,
Expand Down Expand Up @@ -62,6 +69,10 @@ public static void register() {
GuiCaptureRecorder.toggle(client);
return false;
}
if (copyCurrentGuiKey.matches(key)) {
CopyCurrentGui.copyCurrentGui(client);
return false;
}
return true;
});
});
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
package gg.itzkatze.thehypixelrecreationmod.features.worldexport;

import gg.itzkatze.thehypixelrecreationmod.utils.PolarConvert;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import gg.itzkatze.thehypixelrecreationmod.utils.PolarConvert;
import net.minecraft.client.Minecraft;
import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.nbt.CompoundTag;
Expand All @@ -12,6 +12,7 @@
import net.minecraft.util.ProblemReporter;
import net.minecraft.world.entity.Display;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.decoration.ItemFrame;
import net.minecraft.world.level.storage.TagValueOutput;

import java.io.IOException;
Expand All @@ -30,6 +31,7 @@ public final class ChunkExportRecorder {
private static final Map<UUID, CapturedEntity> RECORDED_BLOCK_DISPLAYS = new LinkedHashMap<>();
private static final Map<UUID, CompoundTag> RECORDED_RAVENGARD_OBJECTS = new LinkedHashMap<>();
private static final Set<UUID> MOVING_ENTITIES = new HashSet<>();
private static final Map<UUID, BedWarsSprayPosition> RECORDED_SPRAY_POSITIONS = new LinkedHashMap<>();

private static LoadedChunkExporter.SessionContext sessionContext;
private static Instant startedAt;
Expand Down Expand Up @@ -61,6 +63,7 @@ public static StartResult start(CaptureMode mode, String sessionName) {
RECORDED_BLOCK_DISPLAYS.clear();
RECORDED_RAVENGARD_OBJECTS.clear();
MOVING_ENTITIES.clear();
RECORDED_SPRAY_POSITIONS.clear();
captureMode = mode;
stitchedSessionName = sessionName == null ? null : LoadedChunkExporter.sanitizeSessionName(sessionName);
try {
Expand Down Expand Up @@ -107,6 +110,9 @@ public static StopResult stop(String sessionName) throws IOException {
if (captureMode == CaptureMode.RAVENGARD) {
writeRavengardConfiguration(polarResult.path(), RECORDED_RAVENGARD_OBJECTS.values());
}
if (captureMode == CaptureMode.BEDWARS) {
writeBedWarsConfiguration(polarResult.path());
}
if (stitchedSessionName != null) saveCheckpoint(stitchedSessionName);
clearSession();
return new StopResult(
Expand Down Expand Up @@ -171,6 +177,15 @@ private static void captureCurrentWorld(ClientLevel level, boolean requireMatchi
if (captureMode == CaptureMode.BLOCK_DISPLAYS || captureMode == CaptureMode.RAVENGARD) {
captureBlockDisplays(level);
}
if (captureMode == CaptureMode.BEDWARS) captureSprayPositions(level);
}
}

private static void captureSprayPositions(ClientLevel level) {
for (Entity entity : level.entitiesForRendering()) {
if (!(entity instanceof ItemFrame frame)) continue;
RECORDED_SPRAY_POSITIONS.put(frame.getUUID(), new BedWarsSprayPosition(
frame.getBlockX(), frame.getBlockY(), frame.getBlockZ(), frame.getDirection().getName()));
}
}

Expand Down Expand Up @@ -226,6 +241,7 @@ private static void clearSession() {
RECORDED_BLOCK_DISPLAYS.clear();
RECORDED_RAVENGARD_OBJECTS.clear();
MOVING_ENTITIES.clear();
RECORDED_SPRAY_POSITIONS.clear();
sessionContext = null;
startedAt = null;
ticksUntilCapture = 0;
Expand All @@ -252,6 +268,13 @@ private static void writeRavengardConfiguration(Path polarPath, Collection<Compo
id, id, polarFile, new RavengardPosition(0.5, 65, 0.5, 0, 0), values)));
}

private static void writeBedWarsConfiguration(Path polarPath) throws IOException {
Path configurationPath = polarPath.resolveSibling(
polarPath.getFileName().toString().replaceFirst("\\.polar$", "") + "-bedwars.json");
Files.writeString(configurationPath, GSON.toJson(Map.of(
"sprays", RECORDED_SPRAY_POSITIONS.values())));
}

private static Path checkpointPath(String name) {
return Minecraft.getInstance().gameDirectory.toPath().resolve("chunkexporter_sessions").resolve(name + ".nbt");
}
Expand Down Expand Up @@ -331,7 +354,8 @@ private static double number(ListTag values, int index) {
public enum CaptureMode {
CHUNKS,
BLOCK_DISPLAYS,
RAVENGARD
RAVENGARD,
BEDWARS
}

public record StartResult(String dimension, int initialChunkCount, int initialBlockDisplayCount, CaptureMode mode) {
Expand Down Expand Up @@ -368,6 +392,9 @@ private record RavengardDungeonObject(String category, String type, double x, do
float yaw, float pitch) {
}

private record BedWarsSprayPosition(int x, int y, int z, String facing) {
}

private record EntityPosition(double x, double y, double z) {
double distanceSqr(EntityPosition other) {
double dx = x - other.x;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,14 @@ public static class MapConfiguration {
private Map<TeamKey, MapTeam> teams;
private MapLocations locations;
private Map<GlobalGeneratorKey, GlobalGenerator> globalGenerator;
private List<SprayPosition> sprays;

@Getter
@Setter
public static class MapLocations {
private HypixelPosition waiting;
private HypixelPosition spectator;
private CuboidPosition waitingLobby;
}

@Getter
Expand All @@ -61,6 +63,12 @@ public static class GlobalGenerator {
public record TwoBlockPosition(Vec3i feet, Vec3i head) {
}

public record CuboidPosition(Vec3i min, Vec3i max) {
}

public record SprayPosition(int x, int y, int z, String facing) {
}

public record MinMax(double min, double max) {
}

Expand All @@ -70,7 +78,7 @@ public enum GlobalGeneratorKey {

public enum TeamKey {
RED("Red", "§c", 0xFF5555, Material.RED_BED, Material.RED_WOOL),
BLUE("Blue", "§9", 0x5555FF, Material.BLUE_BED, Material.BLUE_BED),
BLUE("Blue", "§9", 0x5555FF, Material.BLUE_BED, Material.BLUE_WOOL),
GREEN("Green", "§a", 0x55FF55, Material.LIME_BED, Material.LIME_WOOL),
YELLOW("Yellow", "§e", 0xFFFF55, Material.YELLOW_BED, Material.YELLOW_WOOL),
AQUA("Aqua", "§b", 0x00AAAA, Material.LIGHT_BLUE_BED, Material.LIGHT_BLUE_WOOL),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import net.swofty.commons.protocol.JacksonSerializer;
import net.swofty.commons.protocol.RedisProtocol;
import net.swofty.commons.protocol.Serializer;
import net.swofty.commons.replay.protocol.ReplaySection;

import java.util.UUID;

Expand All @@ -22,12 +23,24 @@ public Serializer<BatchResponse> getReturnSerializer() {

public record BatchMessage(
UUID replayId,
int batchIndex,
ReplaySection section,
int sequence,
int startTick,
int endTick,
int recordableCount,
byte[] compressedData
) {}
int uncompressedLength,
int recordCount,
int checksum,
byte[] compressedPayload
) {
public BatchMessage {
compressedPayload = compressedPayload.clone();
}

@Override
public byte[] compressedPayload() {
return compressedPayload.clone();
}
}

public record BatchResponse(boolean success, long bytesReceived) {}
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
package net.swofty.commons.protocol.objects.replay;

import net.swofty.commons.ServerType;
import net.swofty.commons.protocol.JacksonSerializer;
import net.swofty.commons.protocol.RedisProtocol;
import net.swofty.commons.protocol.Serializer;
import net.swofty.commons.replay.protocol.ReplayChunk;

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

public class ReplayLoadProtocolObject extends RedisProtocol<
Expand All @@ -28,38 +27,7 @@ public record LoadRequest(UUID replayId) {}
public record LoadResponse(
boolean success,
String errorMessage,
ReplayMetadata metadata,
List<DataChunk> dataChunks
) {}

public record ReplayMetadata(
UUID replayId,
String gameId,
ServerType serverType,
String serverId,
String gameTypeName,
String mapName,
String mapHash,
int version,
long startTime,
long endTime,
int durationTicks,
Map<UUID, String> players,
Map<String, List<UUID>> teams,
Map<String, TeamInfo> teamInfo,
String winnerId,
String winnerType,
long dataSize,
double mapCenterX,
double mapCenterZ
) {}

public record TeamInfo(String name, String colorCode, int color) {}

public record DataChunk(
int chunkIndex,
int startTick,
int endTick,
byte[] compressedData
ReplayProtocolDto.Metadata metadata,
List<ReplayChunk> chunks
) {}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
package net.swofty.commons.protocol.objects.replay;

import net.swofty.commons.ServerType;

import java.util.List;
import java.util.UUID;

public final class ReplayProtocolDto {
private ReplayProtocolDto() {
}

public record Descriptor(
UUID replayId,
String gameId,
String gameType,
ServerType serverType,
String serverId,
String mapName,
String mapHash,
double mapCenterX,
double mapCenterZ,
int formatVersion,
long startTime,
long endTime,
int durationTicks,
long dataSize
) {
}

public record Participant(
UUID uuid,
int entityId,
String username,
String textureValue,
String textureSignature,
String displayNameJson,
String prefixJson,
String suffixJson
) {
}

public record GameMetadataEnvelope(String gameType, int schemaVersion, byte[] payload) {
public GameMetadataEnvelope {
if (gameType == null || gameType.isBlank())
throw new IllegalArgumentException("gameType must not be blank");
if (schemaVersion < 1) throw new IllegalArgumentException("schemaVersion must be positive");
if (payload.length > 16 * 1024 * 1024)
throw new IllegalArgumentException("Replay game metadata is too large");
payload = payload.clone();
}

@Override
public byte[] payload() {
return payload.clone();
}
}

public record Metadata(Descriptor descriptor, List<Participant> participants, GameMetadataEnvelope gameMetadata) {
public Metadata {
if (participants.size() > 10_000)
throw new IllegalArgumentException("Replay contains too many participants");
participants = List.copyOf(participants);
for (Participant participant : participants) {
validateComponent(participant.displayNameJson());
validateComponent(participant.prefixJson());
validateComponent(participant.suffixJson());
}
}

private static void validateComponent(String component) {
if (component == null || component.length() > 1_048_576) {
throw new IllegalArgumentException("Invalid replay component JSON");
}
}
}
}
Original file line number Diff line number Diff line change
@@ -1,14 +1,9 @@
package net.swofty.commons.protocol.objects.replay;

import net.swofty.commons.ServerType;
import net.swofty.commons.protocol.JacksonSerializer;
import net.swofty.commons.protocol.RedisProtocol;
import net.swofty.commons.protocol.Serializer;

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

public class ReplayStartProtocolObject extends RedisProtocol<
ReplayStartProtocolObject.StartMessage,
ReplayStartProtocolObject.StartResponse> {
Expand All @@ -24,22 +19,9 @@ public Serializer<StartResponse> getReturnSerializer() {
}

public record StartMessage(
UUID replayId,
String gameId,
ServerType serverType,
String serverId,
String gameTypeName,
String mapName,
String mapHash,
long startTime,
double mapCenterX,
double mapCenterZ,
Map<UUID, String> players,
Map<String, List<UUID>> teams,
Map<String, TeamInfo> teamInfo
ReplayProtocolDto.Metadata metadata
) {}

public record StartResponse(boolean success, String message) {}

public record TeamInfo(String name, String colorCode, int color) {}
}
Loading
Loading