diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml
index bafc3aa60..1160f02c1 100644
--- a/.github/workflows/deploy.yml
+++ b/.github/workflows/deploy.yml
@@ -17,9 +17,9 @@ jobs:
cache: 'gradle'
distribution: 'adopt'
- name: Set up server
- run: ./gradlew -PskipBuild=zombies-mapeditor :phantazm-server:setupServer
+ run: ./gradlew -PskipBuild=zombies-mapeditor,zombies-timer :phantazm-server:setupServer
- name: Build & copy jar
- run: ./gradlew -PskipBuild=zombies-mapeditor :phantazm-server:copyJar
+ run: ./gradlew -PskipBuild=zombies-mapeditor,zombies-timer :phantazm-server:copyJar
- name: Zip server files
run: |
cd ./run/server-1
diff --git a/.run/Run Velocity.run.xml b/.run/Run Velocity.run.xml
index cbb04a6e3..f3c46f1ae 100644
--- a/.run/Run Velocity.run.xml
+++ b/.run/Run Velocity.run.xml
@@ -1,15 +1,12 @@
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.run/Run server.run.xml b/.run/Run server.run.xml
index 1589e2f32..694ab74e5 100644
--- a/.run/Run server.run.xml
+++ b/.run/Run server.run.xml
@@ -6,7 +6,7 @@
-
+
\ No newline at end of file
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index e17d086a1..0e670320b 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -28,6 +28,8 @@ examination-string = { module = "net.kyori:examination-string", version.ref = "e
caffeine = { module = "com.github.ben-manes.caffeine:caffeine", version = "3.1.1" }
+cloth-config = { module = "me.shedaniel.cloth:cloth-config-fabric", version = "11.1.106" }
+
commons-lang3 = { module = "org.apache.commons:commons-lang3", version = "3.12.0" }
element-core = { module = "com.github.steanky:element-core", version = "0.14.3" }
@@ -40,7 +42,8 @@ ethylene-yaml = { module = "com.github.steanky:ethylene-yaml", version.ref = "et
ethylene-mapper = { module = "com.github.steanky:ethylene-mapper", version.ref = "ethylene" }
fabric-loader = { module = "net.fabricmc:fabric-loader", version = "0.14.21" }
-fabric-api = { module = "net.fabricmc.fabric-api:fabric-api", version = "0.83.0+1.19.4" }
+fabric-api-oneNineteen = { module = "net.fabricmc.fabric-api:fabric-api", version = "0.83.0+1.19.4" }
+fabric-api-oneTwenty = { module = "net.fabricmc.fabric-api:fabric-api", version = "0.85.0+1.20.1" }
fastutil = { module = "it.unimi.dsi:fastutil", version = "8.5.8" }
@@ -60,7 +63,9 @@ libgui = { module = "io.github.cottonmc:LibGui", version = "7.1.0+1.19.4" }
mariadb = { module = "org.mariadb.jdbc:mariadb-java-client", version = "3.0.10" }
-minecraft = { module = "com.mojang:minecraft", version = "1.19.4" }
+minecraft-oneNineteen = { module = "com.mojang:minecraft", version = "1.19.4" }
+
+minecraft-oneTwenty = { module = "com.mojang:minecraft", version = "1.20.1" }
minestom = { module = "org.phantazm.Minestom:Minestom", version.ref = "minestom" }
@@ -68,6 +73,8 @@ minestom-testing = { module = "org.phantazm.Minestom:testing", version.ref = "mi
mockito-junit-jupiter = { module = "org.mockito:mockito-junit-jupiter", version = "4.7.0" }
+modmenu = { module = "com.terraformersmc:modmenu", version = "7.1.0" }
+
proxima-core = { module = "com.github.steanky:proxima-core", version = "0.6.4" }
renderer = { module = "com.github.0x3C50:Renderer", version = "e1bb03f45e" }
@@ -83,7 +90,9 @@ vector-core = { module = "com.github.steanky:vector-core", version = "0.9.2" }
velocity-api = { module = "com.velocitypowered:velocity-api", version = "3.1.2-SNAPSHOT" }
-yarn-mappings = { module = "net.fabricmc:yarn", version = "1.19.4+build.2" }
+yarn-mappings-oneNineteen = { module = "net.fabricmc:yarn", version = "1.19.4+build.2" }
+
+yarn-mappings-oneTwenty = { module = "net.fabricmc:yarn", version = "1.20.1+build.9" }
[plugins]
fabric-loom = { id = "fabric-loom", version = "1.1.9" }
diff --git a/messaging/src/main/java/org/phantazm/messaging/packet/PacketHandler.java b/messaging/src/main/java/org/phantazm/messaging/packet/PacketHandler.java
index a1481c92d..48250f55b 100644
--- a/messaging/src/main/java/org/phantazm/messaging/packet/PacketHandler.java
+++ b/messaging/src/main/java/org/phantazm/messaging/packet/PacketHandler.java
@@ -50,7 +50,7 @@ public void handleData(@NotNull TOutputReceiver outputReceiver, byte @NotNull []
* @param outputReceiver The output receiver
* @param packet The packet to send
*/
- protected void output(@NotNull TOutputReceiver outputReceiver, @NotNull Packet packet) {
+ public void output(@NotNull TOutputReceiver outputReceiver, @NotNull Packet packet) {
sendToReceiver(outputReceiver, packetSerializer.serializePacket(packet));
}
diff --git a/messaging/src/main/java/org/phantazm/messaging/packet/c2s/RoundStartPacket.java b/messaging/src/main/java/org/phantazm/messaging/packet/c2s/RoundStartPacket.java
new file mode 100644
index 000000000..9ed2553c8
--- /dev/null
+++ b/messaging/src/main/java/org/phantazm/messaging/packet/c2s/RoundStartPacket.java
@@ -0,0 +1,28 @@
+package org.phantazm.messaging.packet.c2s;
+
+import org.jetbrains.annotations.NotNull;
+import org.phantazm.messaging.packet.Packet;
+import org.phantazm.messaging.serialization.DataReader;
+import org.phantazm.messaging.serialization.DataWriter;
+
+import java.util.Objects;
+
+public record RoundStartPacket() implements Packet {
+
+ public static final byte ID = 0;
+
+ public static @NotNull RoundStartPacket read(@NotNull DataReader reader) {
+ Objects.requireNonNull(reader, "reader");
+ return new RoundStartPacket();
+ }
+
+ @Override
+ public byte getId() {
+ return ID;
+ }
+
+ @Override
+ public void write(@NotNull DataWriter dataWriter) {
+
+ }
+}
diff --git a/messaging/src/main/java/org/phantazm/messaging/serialization/PacketSerializers.java b/messaging/src/main/java/org/phantazm/messaging/serialization/PacketSerializers.java
index bfd064984..e1a24a449 100644
--- a/messaging/src/main/java/org/phantazm/messaging/serialization/PacketSerializers.java
+++ b/messaging/src/main/java/org/phantazm/messaging/serialization/PacketSerializers.java
@@ -4,8 +4,8 @@
import org.phantazm.messaging.packet.Packet;
import org.phantazm.messaging.packet.c2p.MapDataVersionQueryPacket;
import org.phantazm.messaging.packet.c2p.MapDataVersionResponsePacket;
+import org.phantazm.messaging.packet.c2s.RoundStartPacket;
-import java.util.Collections;
import java.util.Map;
import java.util.function.Function;
import java.util.function.Supplier;
@@ -15,9 +15,12 @@
*/
public final class PacketSerializers {
+ private static final Map> clientToServerDeserializers;
+
private static final Map> clientToProxyDeserializers;
static {
+ clientToServerDeserializers = Map.of(RoundStartPacket.ID, RoundStartPacket::read);
clientToProxyDeserializers =
Map.of(MapDataVersionQueryPacket.ID, MapDataVersionQueryPacket::read, MapDataVersionResponsePacket.ID,
MapDataVersionResponsePacket::read);
@@ -29,7 +32,7 @@ private PacketSerializers() {
public static @NotNull PacketSerializer clientToServerSerializer(@NotNull Supplier writerCreator,
@NotNull Function readerCreator) {
- return new PacketSerializer(Collections.emptyMap(), writerCreator, readerCreator);
+ return new PacketSerializer(clientToServerDeserializers, writerCreator, readerCreator);
}
public static @NotNull PacketSerializer clientToProxySerializer(@NotNull Supplier writerCreator,
diff --git a/server/src/main/java/org/phantazm/server/MessagingFeature.java b/server/src/main/java/org/phantazm/server/MessagingFeature.java
index c060a0313..d42ff26bc 100644
--- a/server/src/main/java/org/phantazm/server/MessagingFeature.java
+++ b/server/src/main/java/org/phantazm/server/MessagingFeature.java
@@ -14,7 +14,6 @@
import org.phantazm.messaging.packet.PacketHandler;
import org.phantazm.messaging.serialization.PacketSerializer;
import org.phantazm.messaging.serialization.PacketSerializers;
-import org.phantazm.server.config.server.AuthType;
import org.phantazm.server.packet.BinaryDataReader;
import org.phantazm.server.packet.BinaryDataWriter;
@@ -25,27 +24,30 @@
*/
public final class MessagingFeature {
+ private static PacketHandler clientToServerHandler;
+
private MessagingFeature() {
throw new UnsupportedOperationException();
}
- static void initialize(@NotNull EventNode global, @NotNull AuthType authType) {
+ static void initialize(@NotNull EventNode global) {
PacketSerializer clientToServer =
PacketSerializers.clientToServerSerializer(() -> new BinaryDataWriter(new BinaryWriter()),
data -> new BinaryDataReader(new BinaryReader(data)));
Key clientToServerIdentifier = Key.key(Namespaces.PHANTAZM, MessageChannels.CLIENT_TO_SERVER);
- Map> packetHandlers =
- Map.of(MessageChannels.CLIENT_TO_SERVER, new PacketHandler<>(clientToServer) {
- @Override
- protected void handlePacket(@NotNull Player player, @NotNull Packet packet) {
+ clientToServerHandler = new PacketHandler<>(clientToServer) {
+ @Override
+ protected void handlePacket(@NotNull Player player, @NotNull Packet packet) {
- }
+ }
- @Override
- protected void sendToReceiver(@NotNull Player player, byte @NotNull [] data) {
- player.sendPluginMessage(clientToServerIdentifier.toString(), data);
- }
- });
+ @Override
+ protected void sendToReceiver(@NotNull Player player, byte @NotNull [] data) {
+ player.sendPluginMessage(clientToServerIdentifier.toString(), data);
+ }
+ };
+ Map> packetHandlers =
+ Map.of(MessageChannels.CLIENT_TO_SERVER, clientToServerHandler);
global.addListener(PlayerPluginMessageEvent.class, event -> {
String identifier = event.getIdentifier();
@@ -67,4 +69,7 @@ protected void sendToReceiver(@NotNull Player player, byte @NotNull [] data) {
});
}
+ public static @NotNull PacketHandler getClientToServerHandler() {
+ return FeatureUtils.check(clientToServerHandler);
+ }
}
diff --git a/server/src/main/java/org/phantazm/server/PhantazmServer.java b/server/src/main/java/org/phantazm/server/PhantazmServer.java
index cedea224d..dbbab1024 100644
--- a/server/src/main/java/org/phantazm/server/PhantazmServer.java
+++ b/server/src/main/java/org/phantazm/server/PhantazmServer.java
@@ -244,7 +244,7 @@ private static void initializeFeatures(KeyParser keyParser, EventNode glo
LobbyFeature.initialize(global, viewProvider, lobbiesConfig, contextManager);
ChatFeature.initialize(global, viewProvider, chatConfig, PartyFeature.getPartyHolder().uuidToGuild(),
MinecraftServer.getCommandManager());
- MessagingFeature.initialize(global, serverConfig.serverInfoConfig().authType());
+ MessagingFeature.initialize(global);
CommandFeature.initialize(MinecraftServer.getCommandManager(), routerStore, viewProvider,
LobbyFeature.getFallback());
diff --git a/server/src/main/java/org/phantazm/server/ZombiesFeature.java b/server/src/main/java/org/phantazm/server/ZombiesFeature.java
index 6b59a63df..66d946b5a 100644
--- a/server/src/main/java/org/phantazm/server/ZombiesFeature.java
+++ b/server/src/main/java/org/phantazm/server/ZombiesFeature.java
@@ -180,10 +180,12 @@ static void initialize(@NotNull EventNode globalEventNode, @NotNull Conte
return new InstanceClientBlockHandler(instance, dimensionType.getMinY(),
dimensionType.getHeight());
}, globalEventNode), contextManager, keyParser, mobNoPushTeam, corpseTeam, database,
- ZombiesFeature.powerups(), new BasicZombiesPlayerSource(database, viewProvider,
- EquipmentFeature::createEquipmentCreator, MobFeature.getModels(), contextManager,
- keyParser), mapDependencyProvider -> contextManager.makeContext(entry.getValue().corpse())
- .provide(mapDependencyProvider), songLoader);
+ MessagingFeature.getClientToServerHandler(), ZombiesFeature.powerups(),
+ new BasicZombiesPlayerSource(database, viewProvider,
+ EquipmentFeature::createEquipmentCreator, MobFeature.getModels(), contextManager,
+ keyParser),
+ mapDependencyProvider -> contextManager.makeContext(entry.getValue().corpse())
+ .provide(mapDependencyProvider), songLoader);
providers.put(entry.getKey(), provider);
}
diff --git a/settings.gradle.kts b/settings.gradle.kts
index 7ac02d801..a11c2cd7d 100644
--- a/settings.gradle.kts
+++ b/settings.gradle.kts
@@ -33,6 +33,7 @@ sequenceOf(
"proxima-minestom",
"server",
"stats",
+ "zombies-timer",
"velocity",
"zombies",
"zombies-mapdata",
diff --git a/zombies-mapeditor/build.gradle.kts b/zombies-mapeditor/build.gradle.kts
index 57f78bc2d..ce15174bc 100644
--- a/zombies-mapeditor/build.gradle.kts
+++ b/zombies-mapeditor/build.gradle.kts
@@ -1,7 +1,7 @@
// https://youtrack.jetbrains.com/issue/KTIJ-19369/False-positive-can-t-be-called-in-this-context-by-implicit-recei
@Suppress("DSL_SCOPE_VIOLATION")
plugins {
- id("phantazm.java-library-conventions")
+ id("phantazm.java-conventions")
alias(libs.plugins.fabric.loom)
}
@@ -33,8 +33,8 @@ repositories {
}
dependencies {
- minecraft(libs.minecraft)
- mappings(libs.yarn.mappings) {
+ minecraft(libs.minecraft.oneNineteen)
+ mappings(libs.yarn.mappings.oneNineteen) {
artifact {
classifier = "v2"
}
@@ -42,7 +42,7 @@ dependencies {
modImplementation(libs.fabric.loader)
modImplementation(libs.libgui)
- modImplementation(libs.fabric.api)
+ modImplementation(libs.fabric.api.oneNineteen)
modImplementation(libs.renderer)
implementation(projects.phantazmCommons)
@@ -83,8 +83,6 @@ tasks.compileJava {
tasks.jar {
from("../LICENSE") {
- rename {
- "${it}_${base.archivesName.get()}"
- }
+ rename { "${it}_${archiveBaseName.get()}" }
}
}
diff --git a/zombies-mapeditor/src/main/java/org/phantazm/zombies/mapeditor/client/MapeditorClient.java b/zombies-mapeditor/src/main/java/org/phantazm/zombies/mapeditor/client/MapeditorClient.java
index 19a88d6a8..c95e87462 100644
--- a/zombies-mapeditor/src/main/java/org/phantazm/zombies/mapeditor/client/MapeditorClient.java
+++ b/zombies-mapeditor/src/main/java/org/phantazm/zombies/mapeditor/client/MapeditorClient.java
@@ -13,6 +13,7 @@
import net.fabricmc.fabric.api.client.networking.v1.ClientPlayConnectionEvents;
import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking;
import net.fabricmc.fabric.api.event.player.UseBlockCallback;
+import net.fabricmc.fabric.api.networking.v1.PacketByteBufs;
import net.fabricmc.fabric.api.networking.v1.PacketSender;
import net.fabricmc.loader.api.FabricLoader;
import net.kyori.adventure.key.Key;
@@ -24,7 +25,6 @@
import net.minecraft.client.util.InputUtil;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.network.PacketByteBuf;
-import net.minecraft.network.packet.c2s.play.CustomPayloadC2SPacket;
import net.minecraft.text.Text;
import net.minecraft.util.Formatting;
import net.minecraft.util.Identifier;
@@ -44,6 +44,7 @@
import org.phantazm.zombies.map.MapSettingsInfo;
import org.phantazm.zombies.mapeditor.client.packet.PacketByteBufDataReader;
import org.phantazm.zombies.mapeditor.client.packet.PacketByteBufDataWriter;
+import org.phantazm.zombies.mapeditor.client.packet.PhantazmPacket;
import org.phantazm.zombies.mapeditor.client.render.ObjectRenderer;
import org.phantazm.zombies.mapeditor.client.ui.MainGui;
import org.phantazm.zombies.mapeditor.client.ui.NewObjectGui;
@@ -89,16 +90,10 @@ public void onInitializeClient() {
TranslationKeys.CATEGORY_MAPEDITOR_ALL));
PacketSerializer clientToProxy = PacketSerializers.clientToProxySerializer(
- () -> new PacketByteBufDataWriter(new PacketByteBuf(Unpooled.buffer())),
+ () -> new PacketByteBufDataWriter(PacketByteBufs.create()),
data -> new PacketByteBufDataReader(new PacketByteBuf(Unpooled.wrappedBuffer(data))));
Identifier clientToProxyIdentifier = Identifier.of(Namespaces.PHANTAZM, MessageChannels.CLIENT_TO_PROXY);
if (clientToProxyIdentifier != null) {
- ClientPlayConnectionEvents.JOIN.register(((handler, sender, client) -> {
- byte[] data = clientToProxy.serializePacket(new MapDataVersionQueryPacket());
- sender.sendPacket(new CustomPayloadC2SPacket(clientToProxyIdentifier,
- new PacketByteBuf(Unpooled.wrappedBuffer(data))));
- }));
-
PacketHandler clientToProxyHandler = new PacketHandler<>(clientToProxy) {
@Override
protected void handlePacket(@NotNull PacketSender packetSender, @NotNull Packet packet) {
@@ -125,10 +120,12 @@ protected void sendToReceiver(@NotNull PacketSender packetSender, byte @NotNull
packetSender.sendPacket(clientToProxyIdentifier, new PacketByteBuf(Unpooled.wrappedBuffer(data)));
}
};
- ClientPlayNetworking.registerGlobalReceiver(clientToProxyIdentifier,
- (client, handler, buf, responseSender) -> {
- clientToProxyHandler.handleData(responseSender, buf.getWrittenBytes());
- });
+ ClientPlayConnectionEvents.JOIN.register(((handler, sender, client) -> {
+ clientToProxyHandler.output(sender, new MapDataVersionQueryPacket());
+ }));
+ ClientPlayNetworking.registerGlobalReceiver(PhantazmPacket.TYPE, ((packet, player, responseSender) -> {
+ clientToProxyHandler.handleData(responseSender, packet.data());
+ }));
}
ClientTickEvents.END_CLIENT_TICK.register(new ClientTickEvents.EndTick() {
diff --git a/zombies-mapeditor/src/main/java/org/phantazm/zombies/mapeditor/client/packet/PhantazmPacket.java b/zombies-mapeditor/src/main/java/org/phantazm/zombies/mapeditor/client/packet/PhantazmPacket.java
new file mode 100644
index 000000000..1e6b64cd0
--- /dev/null
+++ b/zombies-mapeditor/src/main/java/org/phantazm/zombies/mapeditor/client/packet/PhantazmPacket.java
@@ -0,0 +1,31 @@
+package org.phantazm.zombies.mapeditor.client.packet;
+
+import net.fabricmc.fabric.api.networking.v1.FabricPacket;
+import net.fabricmc.fabric.api.networking.v1.PacketType;
+import net.minecraft.network.PacketByteBuf;
+import net.minecraft.util.Identifier;
+import org.jetbrains.annotations.NotNull;
+import org.phantazm.commons.Namespaces;
+import org.phantazm.messaging.MessageChannels;
+
+public record PhantazmPacket(byte @NotNull [] data) implements FabricPacket {
+
+ public static final PacketType TYPE =
+ PacketType.create(new Identifier(Namespaces.PHANTAZM, MessageChannels.CLIENT_TO_SERVER),
+ PhantazmPacket::new);
+
+ public PhantazmPacket(PacketByteBuf buf) {
+ this(buf.getWrittenBytes());
+ }
+
+ @Override
+ public void write(PacketByteBuf buf) {
+ buf.writeBytes(data);
+ }
+
+ @Override
+ public PacketType> getType() {
+ return TYPE;
+ }
+
+}
diff --git a/zombies-timer/build.gradle.kts b/zombies-timer/build.gradle.kts
new file mode 100644
index 000000000..dfb9eac8a
--- /dev/null
+++ b/zombies-timer/build.gradle.kts
@@ -0,0 +1,59 @@
+// https://youtrack.jetbrains.com/issue/KTIJ-19369/False-positive-can-t-be-called-in-this-context-by-implicit-recei
+@Suppress("DSL_SCOPE_VIOLATION")
+plugins {
+ id("phantazm.java-conventions")
+
+ alias(libs.plugins.fabric.loom)
+}
+
+version = "0.1.0+1.20.1"
+
+base {
+ archivesName.set("zombies-autosplits")
+}
+
+repositories {
+ mavenCentral()
+ maven("https://maven.shedaniel.me/")
+ maven("https://maven.terraformersmc.com/releases/")
+}
+
+dependencies {
+ minecraft(libs.minecraft.oneTwenty)
+ mappings(libs.yarn.mappings.oneTwenty) {
+ artifact {
+ classifier = "v2"
+ }
+ }
+
+ modImplementation(libs.fabric.loader)
+ modImplementation(libs.fabric.api.oneTwenty)
+ modImplementation(libs.modmenu)
+ modImplementation(libs.cloth.config)
+
+ implementation(projects.phantazmCommons)
+ implementation(projects.phantazmMessaging)
+ implementation(libs.ethylene.yaml)
+
+ include(projects.phantazmCommons)
+ include(projects.phantazmMessaging)
+ include(libs.ethylene.core)
+ include(libs.ethylene.yaml)
+ include(libs.toolkit.collection)
+ include(libs.toolkit.function)
+ include(libs.snakeyaml)
+}
+
+tasks.processResources {
+ inputs.property("version", project.version)
+
+ filesMatching("fabric.mod.json") {
+ expand("version" to project.version)
+ }
+}
+
+tasks.jar {
+ from("../LICENSE") {
+ rename { "${it}_${archiveBaseName.get()}" }
+ }
+}
diff --git a/zombies-timer/src/main/java/org/phantazm/zombies/autosplits/ZombiesAutoSplitsClient.java b/zombies-timer/src/main/java/org/phantazm/zombies/autosplits/ZombiesAutoSplitsClient.java
new file mode 100644
index 000000000..ed83354ac
--- /dev/null
+++ b/zombies-timer/src/main/java/org/phantazm/zombies/autosplits/ZombiesAutoSplitsClient.java
@@ -0,0 +1,213 @@
+package org.phantazm.zombies.autosplits;
+
+import com.github.steanky.ethylene.codec.yaml.YamlCodec;
+import com.github.steanky.ethylene.core.BasicConfigHandler;
+import com.github.steanky.ethylene.core.ConfigCodec;
+import com.github.steanky.ethylene.core.ConfigHandler;
+import com.github.steanky.ethylene.core.loader.ConfigLoader;
+import com.github.steanky.ethylene.core.loader.SyncFileConfigLoader;
+import com.github.steanky.ethylene.core.processor.ConfigProcessor;
+import io.netty.buffer.Unpooled;
+import net.fabricmc.api.ClientModInitializer;
+import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientLifecycleEvents;
+import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents;
+import net.fabricmc.fabric.api.client.keybinding.v1.KeyBindingHelper;
+import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking;
+import net.fabricmc.fabric.api.client.rendering.v1.HudRenderCallback;
+import net.fabricmc.fabric.api.networking.v1.PacketByteBufs;
+import net.fabricmc.fabric.api.networking.v1.PacketSender;
+import net.fabricmc.loader.api.FabricLoader;
+import net.minecraft.client.MinecraftClient;
+import net.minecraft.client.option.KeyBinding;
+import net.minecraft.network.PacketByteBuf;
+import org.jetbrains.annotations.NotNull;
+import org.lwjgl.glfw.GLFW;
+import org.phantazm.messaging.packet.PacketHandler;
+import org.phantazm.messaging.serialization.DataReader;
+import org.phantazm.messaging.serialization.DataWriter;
+import org.phantazm.messaging.serialization.PacketSerializer;
+import org.phantazm.messaging.serialization.PacketSerializers;
+import org.phantazm.zombies.autosplits.config.ZombiesAutoSplitsConfig;
+import org.phantazm.zombies.autosplits.config.ZombiesAutoSplitsConfigProcessor;
+import org.phantazm.zombies.autosplits.event.ClientSoundCallback;
+import org.phantazm.zombies.autosplits.messaging.PhantazmMessagingHandler;
+import org.phantazm.zombies.autosplits.packet.PacketByteBufDataReader;
+import org.phantazm.zombies.autosplits.packet.PacketByteBufDataWriter;
+import org.phantazm.zombies.autosplits.packet.PhantazmPacket;
+import org.phantazm.zombies.autosplits.render.RenderTimeHandler;
+import org.phantazm.zombies.autosplits.sound.AutoSplitSoundInterceptor;
+import org.phantazm.zombies.autosplits.splitter.CompositeSplitter;
+import org.phantazm.zombies.autosplits.splitter.AutoSplitSplitter;
+import org.phantazm.zombies.autosplits.splitter.internal.InternalSplitter;
+import org.phantazm.zombies.autosplits.splitter.socket.LiveSplitSocketSplitter;
+import org.phantazm.zombies.autosplits.tick.KeyInputHandler;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.Collection;
+import java.util.concurrent.*;
+import java.util.function.Function;
+import java.util.function.Supplier;
+
+public class ZombiesAutoSplitsClient implements ClientModInitializer {
+
+ public static final String MODID = "zombies-autosplits";
+
+ private static ZombiesAutoSplitsClient instance = null;
+
+ private final KeyBinding autoSplitsKeybind =
+ new KeyBinding("Toggle AutoSplits", GLFW.GLFW_KEY_SEMICOLON, "Phantazm");
+
+ private final ExecutorService executor = Executors.newSingleThreadExecutor();
+
+ private final Logger logger = LoggerFactory.getLogger(MODID);
+
+ private final ConfigHandler configHandler = new BasicConfigHandler();
+
+ private final ConfigHandler.ConfigKey configKey =
+ new ConfigHandler.ConfigKey<>(ZombiesAutoSplitsConfig.class, MODID + "_config");
+
+ private final Collection splitters = new CopyOnWriteArrayList<>();
+
+ private RenderTimeHandler renderTimeHandler;
+
+ private ZombiesAutoSplitsConfig config;
+
+ private CompositeSplitter compositeSplitter;
+
+ public static @NotNull ZombiesAutoSplitsClient getInstance() {
+ return instance;
+ }
+
+ private CompletableFuture loadConfigFromFile() {
+ return configHandler.writeDefaults().thenCompose((unused) -> configHandler.loadData(configKey));
+ }
+
+ public @NotNull ZombiesAutoSplitsConfig getConfig() {
+ return config;
+ }
+
+ public void setConfig(@NotNull ZombiesAutoSplitsConfig config) {
+ for (AutoSplitSplitter splitter : splitters) {
+ splitter.cancel();
+ }
+ splitters.clear();
+
+ this.config = config;
+
+ if (config.useLiveSplits()) {
+ splitters.add(new LiveSplitSocketSplitter(executor, config.host(), config.port()));
+ }
+ if (config.useInternal()) {
+ InternalSplitter internalSplitter = new InternalSplitter();
+ splitters.add(internalSplitter);
+ renderTimeHandler.setSplitter(internalSplitter);
+ }
+ else {
+ renderTimeHandler.setSplitter(null);
+ }
+ }
+
+ public void saveConfig() {
+ configHandler.writeData(configKey, config).whenComplete((ignored, throwable) -> {
+ if (throwable != null) {
+ logger.error("Failed to save config", throwable);
+ }
+ });
+ }
+
+ @Override
+ public void onInitializeClient() {
+ renderTimeHandler = new RenderTimeHandler(MinecraftClient.getInstance(), 0xFFFFFF);
+
+ initConfig();
+
+ compositeSplitter = new CompositeSplitter(MinecraftClient.getInstance(), logger, splitters);
+
+ initEvents();
+ initKeyBindings();
+ initPackets();
+
+ instance = this;
+ }
+
+ private void initConfig() {
+ ConfigCodec codec = new YamlCodec();
+ Path configPath = FabricLoader.getInstance().getConfigDir().resolve("zombies-autosplits");
+ String configFileName;
+ if (codec.getPreferredExtensions().isEmpty()) {
+ configFileName = "config";
+ }
+ else {
+ configFileName = "config." + codec.getPreferredExtension();
+ }
+
+ try {
+ Files.createDirectories(configPath);
+ }
+ catch (IOException e) {
+ logger.error("Failed to create config directory", e);
+ return;
+ }
+
+ Path configFile = configPath.resolve(configFileName);
+ ZombiesAutoSplitsConfig defaultConfig = ZombiesAutoSplitsConfig.DEFAULT;
+ ConfigProcessor configProcessor = new ZombiesAutoSplitsConfigProcessor();
+ ConfigLoader configLoader =
+ new SyncFileConfigLoader<>(configProcessor, defaultConfig, configFile, codec);
+ configHandler.registerLoader(configKey, configLoader);
+
+ ZombiesAutoSplitsConfig loadedConfig = loadConfigFromFile().join();
+ setConfig(loadedConfig);
+ }
+
+ private void initEvents() {
+ AutoSplitSoundInterceptor soundInterceptor =
+ new AutoSplitSoundInterceptor(MinecraftClient.getInstance(), compositeSplitter);
+ ClientTickEvents.END_CLIENT_TICK.register(new KeyInputHandler(autoSplitsKeybind, compositeSplitter));
+ HudRenderCallback.EVENT.register(renderTimeHandler);
+ ClientSoundCallback.EVENT.register(soundInterceptor);
+ ClientLifecycleEvents.CLIENT_STOPPING.register(unused -> shutdownExecutor());
+ }
+
+ private void shutdownExecutor() {
+ logger.info("Shutting down executor");
+ executor.shutdown();
+ try {
+ if (executor.awaitTermination(5L, TimeUnit.SECONDS)) {
+ return;
+ }
+
+ logger.warn("Executor did not shut down in 5 seconds. Force shutting down");
+ executor.shutdownNow();
+
+ if (!executor.awaitTermination(5L, TimeUnit.SECONDS)) {
+ logger.warn("Executor did not terminate");
+ }
+ }
+ catch (InterruptedException e) {
+ executor.shutdownNow();
+ Thread.currentThread().interrupt();
+ }
+ }
+
+ private void initKeyBindings() {
+ KeyBindingHelper.registerKeyBinding(autoSplitsKeybind);
+ }
+
+ private void initPackets() {
+ Supplier writerCreator = () -> new PacketByteBufDataWriter(PacketByteBufs.create());
+ Function readerCreator =
+ data -> new PacketByteBufDataReader(new PacketByteBuf(Unpooled.wrappedBuffer(data)));
+ PacketSerializer packetSerializer = PacketSerializers.clientToServerSerializer(writerCreator, readerCreator);
+ PacketHandler clientToServerHandler =
+ new PhantazmMessagingHandler(packetSerializer, PhantazmPacket.TYPE.getId(), compositeSplitter);
+ ClientPlayNetworking.registerGlobalReceiver(PhantazmPacket.TYPE, (packet, player, responseSender) -> {
+ clientToServerHandler.handleData(responseSender, packet.data());
+ });
+ }
+
+}
diff --git a/zombies-timer/src/main/java/org/phantazm/zombies/autosplits/config/ZombiesAutoSplitsConfig.java b/zombies-timer/src/main/java/org/phantazm/zombies/autosplits/config/ZombiesAutoSplitsConfig.java
new file mode 100644
index 000000000..9c12679b9
--- /dev/null
+++ b/zombies-timer/src/main/java/org/phantazm/zombies/autosplits/config/ZombiesAutoSplitsConfig.java
@@ -0,0 +1,65 @@
+package org.phantazm.zombies.autosplits.config;
+
+import org.jetbrains.annotations.NotNull;
+
+import java.util.Objects;
+
+public record ZombiesAutoSplitsConfig(@NotNull String host, int port, boolean useLiveSplits, boolean useInternal) {
+
+ public static final String DEFAULT_HOST = "localhost";
+
+ public static final int DEFAULT_PORT = 16834;
+
+ public static final boolean DEFAULT_USE_LIVE_SPLITS = false;
+
+ public static final boolean DEFAULT_USE_INTERNAL = true;
+
+ public static final ZombiesAutoSplitsConfig DEFAULT =
+ new ZombiesAutoSplitsConfig(DEFAULT_HOST, DEFAULT_PORT, DEFAULT_USE_LIVE_SPLITS, DEFAULT_USE_INTERNAL);
+
+ public ZombiesAutoSplitsConfig {
+ Objects.requireNonNull(host, "host");
+ }
+
+ public @NotNull Builder toBuilder() {
+ return new Builder().setHost(host).setPort(port).setUseLiveSplits(useLiveSplits).setUseInternal(useInternal);
+ }
+
+ public static class Builder {
+
+ private String host = DEFAULT_HOST;
+
+ private int port = DEFAULT_PORT;
+
+ private boolean useLiveSplits = DEFAULT_USE_LIVE_SPLITS;
+
+ private boolean useInternal = DEFAULT_USE_INTERNAL;
+
+ public @NotNull Builder setHost(String host) {
+ Objects.requireNonNull(host, "host");
+ this.host = host;
+ return this;
+ }
+
+ public @NotNull Builder setPort(int port) {
+ this.port = port;
+ return this;
+ }
+
+ public @NotNull Builder setUseLiveSplits(boolean useLiveSplits) {
+ this.useLiveSplits = useLiveSplits;
+ return this;
+ }
+
+ public @NotNull Builder setUseInternal(boolean useInternal) {
+ this.useInternal = useInternal;
+ return this;
+ }
+
+ public @NotNull ZombiesAutoSplitsConfig build() {
+ return new ZombiesAutoSplitsConfig(host, port, useLiveSplits, useInternal);
+ }
+
+ }
+
+}
diff --git a/zombies-timer/src/main/java/org/phantazm/zombies/autosplits/config/ZombiesAutoSplitsConfigProcessor.java b/zombies-timer/src/main/java/org/phantazm/zombies/autosplits/config/ZombiesAutoSplitsConfigProcessor.java
new file mode 100644
index 000000000..f73ba624e
--- /dev/null
+++ b/zombies-timer/src/main/java/org/phantazm/zombies/autosplits/config/ZombiesAutoSplitsConfigProcessor.java
@@ -0,0 +1,27 @@
+package org.phantazm.zombies.autosplits.config;
+
+import com.github.steanky.ethylene.core.ConfigElement;
+import com.github.steanky.ethylene.core.collection.ConfigNode;
+import com.github.steanky.ethylene.core.processor.ConfigProcessException;
+import com.github.steanky.ethylene.core.processor.ConfigProcessor;
+import org.jetbrains.annotations.NotNull;
+
+public class ZombiesAutoSplitsConfigProcessor implements ConfigProcessor {
+ @Override
+ public @NotNull ZombiesAutoSplitsConfig dataFromElement(@NotNull ConfigElement element) throws ConfigProcessException {
+ String host = element.getStringOrThrow("host");
+ int port = element.getNumberOrThrow("port").intValue();
+ if (port < 1 || port > 65535) {
+ throw new ConfigProcessException("Invalid port");
+ }
+ boolean useLiveSplits = element.getBooleanOrThrow("useLiveSplits");
+ boolean useInternal = element.getBooleanOrThrow("useInternal");
+
+ return new ZombiesAutoSplitsConfig(host, port, useLiveSplits, useInternal);
+ }
+
+ @Override
+ public @NotNull ConfigElement elementFromData(@NotNull ZombiesAutoSplitsConfig config) {
+ return ConfigNode.of("host", config.host(), "port", config.port(), "useLiveSplits", config.useLiveSplits(), "useInternal", config.useInternal());
+ }
+}
diff --git a/zombies-timer/src/main/java/org/phantazm/zombies/autosplits/event/ClientSoundCallback.java b/zombies-timer/src/main/java/org/phantazm/zombies/autosplits/event/ClientSoundCallback.java
new file mode 100644
index 000000000..c233c997f
--- /dev/null
+++ b/zombies-timer/src/main/java/org/phantazm/zombies/autosplits/event/ClientSoundCallback.java
@@ -0,0 +1,19 @@
+package org.phantazm.zombies.autosplits.event;
+
+import net.fabricmc.fabric.api.event.Event;
+import net.fabricmc.fabric.api.event.EventFactory;
+import net.minecraft.client.sound.SoundInstance;
+import org.jetbrains.annotations.NotNull;
+
+@FunctionalInterface
+public interface ClientSoundCallback {
+
+ Event EVENT = EventFactory.createArrayBacked(ClientSoundCallback.class, callbacks -> (sound) -> {
+ for (ClientSoundCallback callback : callbacks) {
+ callback.onPlaySound(sound);
+ }
+ });
+
+ void onPlaySound(@NotNull SoundInstance sound);
+
+}
diff --git a/zombies-timer/src/main/java/org/phantazm/zombies/autosplits/messaging/PhantazmMessagingHandler.java b/zombies-timer/src/main/java/org/phantazm/zombies/autosplits/messaging/PhantazmMessagingHandler.java
new file mode 100644
index 000000000..8ebbd75a9
--- /dev/null
+++ b/zombies-timer/src/main/java/org/phantazm/zombies/autosplits/messaging/PhantazmMessagingHandler.java
@@ -0,0 +1,40 @@
+package org.phantazm.zombies.autosplits.messaging;
+
+import io.netty.buffer.Unpooled;
+import net.fabricmc.fabric.api.networking.v1.PacketSender;
+import net.minecraft.network.PacketByteBuf;
+import net.minecraft.util.Identifier;
+import org.jetbrains.annotations.NotNull;
+import org.phantazm.messaging.packet.Packet;
+import org.phantazm.messaging.packet.PacketHandler;
+import org.phantazm.messaging.packet.c2s.RoundStartPacket;
+import org.phantazm.messaging.serialization.PacketSerializer;
+import org.phantazm.zombies.autosplits.splitter.CompositeSplitter;
+
+import java.util.Objects;
+
+public class PhantazmMessagingHandler extends PacketHandler {
+
+ private final CompositeSplitter compositeSplitter;
+
+ private final Identifier identifier;
+
+ public PhantazmMessagingHandler(@NotNull PacketSerializer packetSerializer, @NotNull Identifier identifier,
+ @NotNull CompositeSplitter compositeSplitter) {
+ super(packetSerializer);
+ this.identifier = Objects.requireNonNull(identifier, "identifier");
+ this.compositeSplitter = Objects.requireNonNull(compositeSplitter, "compositeSplitter");
+ }
+
+ @Override
+ protected void handlePacket(@NotNull PacketSender packetSender, @NotNull Packet packet) {
+ if (packet instanceof RoundStartPacket) {
+ compositeSplitter.split();
+ }
+ }
+
+ @Override
+ protected void sendToReceiver(@NotNull PacketSender packetSender, byte @NotNull [] data) {
+ packetSender.sendPacket(identifier, new PacketByteBuf(Unpooled.wrappedBuffer(data)));
+ }
+}
diff --git a/zombies-timer/src/main/java/org/phantazm/zombies/autosplits/mixin/SoundSystemMixin.java b/zombies-timer/src/main/java/org/phantazm/zombies/autosplits/mixin/SoundSystemMixin.java
new file mode 100644
index 000000000..24f473fe9
--- /dev/null
+++ b/zombies-timer/src/main/java/org/phantazm/zombies/autosplits/mixin/SoundSystemMixin.java
@@ -0,0 +1,22 @@
+package org.phantazm.zombies.autosplits.mixin;
+
+import net.fabricmc.api.EnvType;
+import net.fabricmc.api.Environment;
+import net.minecraft.client.sound.SoundInstance;
+import net.minecraft.client.sound.SoundSystem;
+import org.phantazm.zombies.autosplits.event.ClientSoundCallback;
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.injection.At;
+import org.spongepowered.asm.mixin.injection.Inject;
+import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
+
+@Environment(EnvType.CLIENT)
+@Mixin(SoundSystem.class)
+abstract class SoundSystemMixin {
+
+ @Inject(at = @At("HEAD"), method = "play(Lnet/minecraft/client/sound/SoundInstance;)V")
+ private void play(SoundInstance sound, CallbackInfo callbackInfo) {
+ ClientSoundCallback.EVENT.invoker().onPlaySound(sound);
+ }
+
+}
diff --git a/zombies-timer/src/main/java/org/phantazm/zombies/autosplits/modmenu/ZombiesAutoSplitsModMenuApiImpl.java b/zombies-timer/src/main/java/org/phantazm/zombies/autosplits/modmenu/ZombiesAutoSplitsModMenuApiImpl.java
new file mode 100644
index 000000000..b3a7e614f
--- /dev/null
+++ b/zombies-timer/src/main/java/org/phantazm/zombies/autosplits/modmenu/ZombiesAutoSplitsModMenuApiImpl.java
@@ -0,0 +1,60 @@
+package org.phantazm.zombies.autosplits.modmenu;
+
+import com.terraformersmc.modmenu.api.ConfigScreenFactory;
+import com.terraformersmc.modmenu.api.ModMenuApi;
+import me.shedaniel.clothconfig2.api.ConfigBuilder;
+import me.shedaniel.clothconfig2.api.ConfigCategory;
+import me.shedaniel.clothconfig2.api.ConfigEntryBuilder;
+import net.minecraft.text.Text;
+import org.phantazm.zombies.autosplits.ZombiesAutoSplitsClient;
+import org.phantazm.zombies.autosplits.config.ZombiesAutoSplitsConfig;
+
+public class ZombiesAutoSplitsModMenuApiImpl implements ModMenuApi {
+
+ private final ConfigScreenFactory> screenFactory = screen -> {
+ ZombiesAutoSplitsClient autoSplits = ZombiesAutoSplitsClient.getInstance();
+ ZombiesAutoSplitsConfig autoSplitsConfig = autoSplits.getConfig();
+ ZombiesAutoSplitsConfig.Builder autoSplitsConfigBuilder = autoSplitsConfig.toBuilder();
+
+ ConfigBuilder configBuilder = ConfigBuilder.create()
+ .setParentScreen(screen)
+ .setTitle(Text.of("Zombies AutoSplits Config"));
+
+ ConfigEntryBuilder entryBuilder = configBuilder.entryBuilder();
+ ConfigCategory main = configBuilder.getOrCreateCategory(Text.of("Config"));
+ main.addEntry(entryBuilder.startStrField(Text.of("Host"), autoSplitsConfig.host())
+ .setDefaultValue(ZombiesAutoSplitsConfig.DEFAULT_HOST)
+ .setTooltip(Text.of("The host of the LiveSplits server. Most likely localhost."))
+ .setSaveConsumer(autoSplitsConfigBuilder::setHost)
+ .build());
+ main.addEntry(entryBuilder.startIntField(Text.of("Port"), autoSplitsConfig.port())
+ .setDefaultValue(ZombiesAutoSplitsConfig.DEFAULT_PORT)
+ .setMin(1)
+ .setMax(65535)
+ .setTooltip(Text.of("The port of the LiveSplits server. Use -1 for the internal splitter."))
+ .setSaveConsumer(autoSplitsConfigBuilder::setPort)
+ .build());
+ main.addEntry(entryBuilder.startBooleanToggle(Text.of("Use LiveSplits"), autoSplitsConfig.useLiveSplits())
+ .setDefaultValue(ZombiesAutoSplitsConfig.DEFAULT_USE_LIVE_SPLITS)
+ .setTooltip(Text.of("Whether to use the LiveSplits splitter."))
+ .setSaveConsumer(autoSplitsConfigBuilder::setUseLiveSplits)
+ .build());
+ main.addEntry(entryBuilder.startBooleanToggle(Text.of("Use Internal"), autoSplitsConfig.useInternal())
+ .setDefaultValue(ZombiesAutoSplitsConfig.DEFAULT_USE_INTERNAL)
+ .setTooltip(Text.of("Whether to use the internal splitter."))
+ .setSaveConsumer(autoSplitsConfigBuilder::setUseInternal)
+ .build());
+
+ configBuilder.setSavingRunnable(() -> {
+ autoSplits.setConfig(autoSplitsConfigBuilder.build());
+ autoSplits.saveConfig();
+ });
+
+ return configBuilder.build();
+ };
+
+ @Override
+ public ConfigScreenFactory> getModConfigScreenFactory() {
+ return screenFactory;
+ }
+}
diff --git a/zombies-timer/src/main/java/org/phantazm/zombies/autosplits/packet/PacketByteBufDataReader.java b/zombies-timer/src/main/java/org/phantazm/zombies/autosplits/packet/PacketByteBufDataReader.java
new file mode 100644
index 000000000..7d0cdf69b
--- /dev/null
+++ b/zombies-timer/src/main/java/org/phantazm/zombies/autosplits/packet/PacketByteBufDataReader.java
@@ -0,0 +1,34 @@
+package org.phantazm.zombies.autosplits.packet;
+
+import net.minecraft.network.PacketByteBuf;
+import org.jetbrains.annotations.NotNull;
+import org.phantazm.messaging.serialization.DataReader;
+
+import java.util.Objects;
+
+/**
+ * A {@link DataReader} that reads from a {@link PacketByteBuf}.
+ */
+public class PacketByteBufDataReader implements DataReader {
+
+ private final PacketByteBuf packetByteBuf;
+
+ /**
+ * Creates a {@link PacketByteBufDataReader}.
+ *
+ * @param packetByteBuf The {@link PacketByteBuf} to read from
+ */
+ public PacketByteBufDataReader(@NotNull PacketByteBuf packetByteBuf) {
+ this.packetByteBuf = Objects.requireNonNull(packetByteBuf, "packetByteBuf");
+ }
+
+ @Override
+ public byte readByte() {
+ return packetByteBuf.readByte();
+ }
+
+ @Override
+ public int readInt() {
+ return packetByteBuf.readInt();
+ }
+}
diff --git a/zombies-timer/src/main/java/org/phantazm/zombies/autosplits/packet/PacketByteBufDataWriter.java b/zombies-timer/src/main/java/org/phantazm/zombies/autosplits/packet/PacketByteBufDataWriter.java
new file mode 100644
index 000000000..e20bc8094
--- /dev/null
+++ b/zombies-timer/src/main/java/org/phantazm/zombies/autosplits/packet/PacketByteBufDataWriter.java
@@ -0,0 +1,39 @@
+package org.phantazm.zombies.autosplits.packet;
+
+import net.minecraft.network.PacketByteBuf;
+import org.jetbrains.annotations.NotNull;
+import org.phantazm.messaging.serialization.DataWriter;
+
+import java.util.Objects;
+
+/**
+ * A {@link DataWriter} that writes to a {@link PacketByteBuf}.
+ */
+public class PacketByteBufDataWriter implements DataWriter {
+
+ private final PacketByteBuf packetByteBuf;
+
+ /**
+ * Creates a {@link PacketByteBufDataWriter}.
+ *
+ * @param packetByteBuf The {@link PacketByteBuf} to write to
+ */
+ public PacketByteBufDataWriter(@NotNull PacketByteBuf packetByteBuf) {
+ this.packetByteBuf = Objects.requireNonNull(packetByteBuf, "packetByteBuf");
+ }
+
+ @Override
+ public void writeByte(byte data) {
+ packetByteBuf.writeByte(data);
+ }
+
+ @Override
+ public void writeInt(int data) {
+ packetByteBuf.writeInt(data);
+ }
+
+ @Override
+ public byte @NotNull [] toByteArray() {
+ return packetByteBuf.array();
+ }
+}
diff --git a/zombies-timer/src/main/java/org/phantazm/zombies/autosplits/packet/PhantazmPacket.java b/zombies-timer/src/main/java/org/phantazm/zombies/autosplits/packet/PhantazmPacket.java
new file mode 100644
index 000000000..4eaf12491
--- /dev/null
+++ b/zombies-timer/src/main/java/org/phantazm/zombies/autosplits/packet/PhantazmPacket.java
@@ -0,0 +1,31 @@
+package org.phantazm.zombies.autosplits.packet;
+
+import net.fabricmc.fabric.api.networking.v1.FabricPacket;
+import net.fabricmc.fabric.api.networking.v1.PacketType;
+import net.minecraft.network.PacketByteBuf;
+import net.minecraft.util.Identifier;
+import org.jetbrains.annotations.NotNull;
+import org.phantazm.commons.Namespaces;
+import org.phantazm.messaging.MessageChannels;
+
+public record PhantazmPacket(byte @NotNull [] data) implements FabricPacket {
+
+ public static final PacketType TYPE =
+ PacketType.create(new Identifier(Namespaces.PHANTAZM, MessageChannels.CLIENT_TO_SERVER),
+ PhantazmPacket::new);
+
+ public PhantazmPacket(PacketByteBuf buf) {
+ this(buf.getWrittenBytes());
+ }
+
+ @Override
+ public void write(PacketByteBuf buf) {
+ buf.writeBytes(data);
+ }
+
+ @Override
+ public PacketType> getType() {
+ return TYPE;
+ }
+
+}
diff --git a/zombies-timer/src/main/java/org/phantazm/zombies/autosplits/render/RenderTimeHandler.java b/zombies-timer/src/main/java/org/phantazm/zombies/autosplits/render/RenderTimeHandler.java
new file mode 100644
index 000000000..f85bbb386
--- /dev/null
+++ b/zombies-timer/src/main/java/org/phantazm/zombies/autosplits/render/RenderTimeHandler.java
@@ -0,0 +1,55 @@
+package org.phantazm.zombies.autosplits.render;
+
+import net.fabricmc.fabric.api.client.rendering.v1.HudRenderCallback;
+import net.minecraft.client.MinecraftClient;
+import net.minecraft.client.gui.DrawContext;
+import net.minecraft.client.util.Window;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.phantazm.zombies.autosplits.splitter.internal.InternalSplitter;
+
+import java.util.Objects;
+
+public class RenderTimeHandler implements HudRenderCallback {
+
+ private final MinecraftClient client;
+
+ private InternalSplitter internalSplitter;
+
+ private final int color;
+
+ public RenderTimeHandler(@NotNull MinecraftClient client, int color) {
+ this.client = Objects.requireNonNull(client, "client");
+ this.color = color;
+ }
+
+ @Override
+ public void onHudRender(DrawContext drawContext, float tickDelta) {
+ if (internalSplitter == null) {
+ return;
+ }
+
+ String time = getTimeString();
+ int width = client.textRenderer.getWidth(time);
+ Window window = client.getWindow();
+ int screenWidth = window.getScaledWidth();
+ int screenHeight = window.getScaledHeight();
+ drawContext.getMatrices().push();
+ drawContext.drawTextWithShadow(client.textRenderer, time, screenWidth - width,
+ screenHeight - client.textRenderer.fontHeight, color);
+ drawContext.getMatrices().pop();
+ }
+
+ private String getTimeString() {
+ long millis = internalSplitter.getMillis();
+ long minutesPart = millis / 60000;
+ long secondsPart = (millis % 60000) / 1000;
+ long tenthSecondsPart = (millis % 1000) / 100;
+ return String.format("%d:%02d:%d", minutesPart, secondsPart, tenthSecondsPart);
+ }
+
+ public void setSplitter(@Nullable InternalSplitter internalSplitter) {
+ this.internalSplitter = internalSplitter;
+ }
+
+}
diff --git a/zombies-timer/src/main/java/org/phantazm/zombies/autosplits/sound/AutoSplitSoundInterceptor.java b/zombies-timer/src/main/java/org/phantazm/zombies/autosplits/sound/AutoSplitSoundInterceptor.java
new file mode 100644
index 000000000..560a35d88
--- /dev/null
+++ b/zombies-timer/src/main/java/org/phantazm/zombies/autosplits/sound/AutoSplitSoundInterceptor.java
@@ -0,0 +1,45 @@
+package org.phantazm.zombies.autosplits.sound;
+
+import net.minecraft.client.MinecraftClient;
+import net.minecraft.client.sound.SoundInstance;
+import net.minecraft.sound.SoundEvents;
+import net.minecraft.util.Identifier;
+import org.jetbrains.annotations.NotNull;
+import org.phantazm.zombies.autosplits.event.ClientSoundCallback;
+import org.phantazm.zombies.autosplits.splitter.CompositeSplitter;
+
+import java.util.Objects;
+
+public class AutoSplitSoundInterceptor implements ClientSoundCallback {
+
+ private final MinecraftClient client;
+
+ private final CompositeSplitter compositeSplitter;
+
+ public AutoSplitSoundInterceptor(@NotNull MinecraftClient client, @NotNull CompositeSplitter compositeSplitter) {
+ this.client = Objects.requireNonNull(client, "client");
+ this.compositeSplitter = Objects.requireNonNull(compositeSplitter, "compositeSplitter");
+ }
+
+ @Override
+ public void onPlaySound(@NotNull SoundInstance sound) {
+ if (isOnHypixel() && isRoundSound(sound)) {
+ compositeSplitter.split();
+ }
+ }
+
+ private boolean isOnHypixel() {
+ if (client.player == null) {
+ return false;
+ }
+
+ String brand = client.player.getServerBrand();
+ return brand != null && brand.contains("Hypixel");
+ }
+
+ private boolean isRoundSound(SoundInstance sound) {
+ Identifier identifier = sound.getId();
+ return identifier.equals(SoundEvents.ENTITY_WITHER_SPAWN.getId()) || identifier.equals(SoundEvents.ENTITY_ENDER_DRAGON_DEATH.getId());
+ }
+
+}
diff --git a/zombies-timer/src/main/java/org/phantazm/zombies/autosplits/splitter/AutoSplitSplitter.java b/zombies-timer/src/main/java/org/phantazm/zombies/autosplits/splitter/AutoSplitSplitter.java
new file mode 100644
index 000000000..4caa74195
--- /dev/null
+++ b/zombies-timer/src/main/java/org/phantazm/zombies/autosplits/splitter/AutoSplitSplitter.java
@@ -0,0 +1,13 @@
+package org.phantazm.zombies.autosplits.splitter;
+
+import org.jetbrains.annotations.NotNull;
+
+import java.util.concurrent.CompletableFuture;
+
+public interface AutoSplitSplitter {
+
+ @NotNull CompletableFuture startOrSplit();
+
+ void cancel();
+
+}
diff --git a/zombies-timer/src/main/java/org/phantazm/zombies/autosplits/splitter/CompositeSplitter.java b/zombies-timer/src/main/java/org/phantazm/zombies/autosplits/splitter/CompositeSplitter.java
new file mode 100644
index 000000000..894885e5d
--- /dev/null
+++ b/zombies-timer/src/main/java/org/phantazm/zombies/autosplits/splitter/CompositeSplitter.java
@@ -0,0 +1,56 @@
+package org.phantazm.zombies.autosplits.splitter;
+
+import net.minecraft.client.MinecraftClient;
+import net.minecraft.text.MutableText;
+import net.minecraft.text.Text;
+import net.minecraft.util.Formatting;
+import org.jetbrains.annotations.NotNull;
+import org.slf4j.Logger;
+
+import java.util.Collection;
+import java.util.Objects;
+
+public class CompositeSplitter {
+
+ private final MinecraftClient client;
+
+ private final Logger logger;
+
+ private final Collection splitters;
+
+ private boolean enabled = true;
+
+ public CompositeSplitter(@NotNull MinecraftClient client, @NotNull Logger logger,
+ @NotNull Collection splitters) {
+ this.client = Objects.requireNonNull(client, "client");
+ this.logger = Objects.requireNonNull(logger, "logger");
+ this.splitters = Objects.requireNonNull(splitters, "splitters");
+ }
+
+ public void split() {
+ if (!enabled) {
+ return;
+ }
+
+ for (AutoSplitSplitter splitter : splitters) {
+ splitter.startOrSplit().whenComplete((ignored, throwable) -> {
+ if (throwable != null) {
+ logger.warn("Failed to split", throwable);
+ client.execute(this::warnFail);
+ }
+ });
+ }
+ }
+
+ private void warnFail() {
+ if (client.player != null) {
+ MutableText message = Text.literal("Failed to split!").formatted(Formatting.RED);
+ client.player.sendMessage(message, false);
+ }
+ }
+
+ public boolean toggle() {
+ return enabled = !enabled;
+ }
+
+}
diff --git a/zombies-timer/src/main/java/org/phantazm/zombies/autosplits/splitter/internal/InternalSplitter.java b/zombies-timer/src/main/java/org/phantazm/zombies/autosplits/splitter/internal/InternalSplitter.java
new file mode 100644
index 000000000..a03dc9d83
--- /dev/null
+++ b/zombies-timer/src/main/java/org/phantazm/zombies/autosplits/splitter/internal/InternalSplitter.java
@@ -0,0 +1,29 @@
+package org.phantazm.zombies.autosplits.splitter.internal;
+
+import org.jetbrains.annotations.NotNull;
+import org.phantazm.zombies.autosplits.splitter.AutoSplitSplitter;
+
+import java.util.concurrent.CompletableFuture;
+
+public class InternalSplitter implements AutoSplitSplitter {
+
+ private long splitTime = 0L;
+
+ @Override
+ public @NotNull CompletableFuture startOrSplit() {
+ splitTime = System.currentTimeMillis();
+ return CompletableFuture.completedFuture(null);
+ }
+
+ public void cancel() {
+ splitTime = 0L;
+ }
+
+ public long getMillis() {
+ if (splitTime == 0L) {
+ return 0L;
+ }
+
+ return System.currentTimeMillis() - splitTime;
+ }
+}
diff --git a/zombies-timer/src/main/java/org/phantazm/zombies/autosplits/splitter/socket/LiveSplitSocketSplitter.java b/zombies-timer/src/main/java/org/phantazm/zombies/autosplits/splitter/socket/LiveSplitSocketSplitter.java
new file mode 100644
index 000000000..2577faa4d
--- /dev/null
+++ b/zombies-timer/src/main/java/org/phantazm/zombies/autosplits/splitter/socket/LiveSplitSocketSplitter.java
@@ -0,0 +1,53 @@
+package org.phantazm.zombies.autosplits.splitter.socket;
+
+import org.jetbrains.annotations.NotNull;
+import org.phantazm.zombies.autosplits.splitter.AutoSplitSplitter;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.Writer;
+import java.net.Socket;
+import java.util.Objects;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.Executor;
+
+public class LiveSplitSocketSplitter implements AutoSplitSplitter {
+
+ private final Executor executor;
+
+ private final String host;
+
+ private final int port;
+
+ public LiveSplitSocketSplitter(@NotNull Executor executor, @NotNull String host, int port) {
+ this.executor = Objects.requireNonNull(executor, "executor");
+ this.host = Objects.requireNonNull(host, "host");
+ this.port = port;
+ }
+
+ @Override
+ public @NotNull CompletableFuture startOrSplit() {
+ return sendCommand("startorsplit");
+ }
+
+ @Override
+ public void cancel() {
+
+ }
+
+ @SuppressWarnings("SameParameterValue")
+ private CompletableFuture sendCommand(String command) {
+ return CompletableFuture.runAsync(() -> {
+ try (Socket socket = new Socket(host, port);
+ OutputStream outputStream = socket.getOutputStream();
+ Writer writer = new OutputStreamWriter(outputStream)) {
+ writer.write(command + "\r\n");
+ writer.flush();
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }, executor);
+ }
+
+}
diff --git a/zombies-timer/src/main/java/org/phantazm/zombies/autosplits/tick/KeyInputHandler.java b/zombies-timer/src/main/java/org/phantazm/zombies/autosplits/tick/KeyInputHandler.java
new file mode 100644
index 000000000..49f3579ac
--- /dev/null
+++ b/zombies-timer/src/main/java/org/phantazm/zombies/autosplits/tick/KeyInputHandler.java
@@ -0,0 +1,54 @@
+package org.phantazm.zombies.autosplits.tick;
+
+import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents;
+import net.minecraft.client.MinecraftClient;
+import net.minecraft.client.option.KeyBinding;
+import net.minecraft.entity.player.PlayerEntity;
+import net.minecraft.text.Text;
+import net.minecraft.util.Formatting;
+import org.jetbrains.annotations.NotNull;
+import org.phantazm.zombies.autosplits.splitter.CompositeSplitter;
+
+import java.util.Objects;
+
+public class KeyInputHandler implements ClientTickEvents.EndTick {
+
+ private final KeyBinding keyBinding;
+
+ private final CompositeSplitter compositeSplitter;
+
+ public KeyInputHandler(@NotNull KeyBinding keyBinding, @NotNull CompositeSplitter compositeSplitter) {
+ this.keyBinding = Objects.requireNonNull(keyBinding, "keyBinding");
+ this.compositeSplitter = Objects.requireNonNull(compositeSplitter, "compositeSplitter");
+ }
+
+ @Override
+ public void onEndTick(MinecraftClient client) {
+ if (!keyBinding.wasPressed()) {
+ return;
+ }
+
+ Text toggledComponent;
+ boolean toggled = compositeSplitter.toggle();
+
+ PlayerEntity player = client.player;
+ if (player == null) {
+ return;
+ }
+
+ if (toggled) {
+ toggledComponent = Text.literal("ON").formatted(Formatting.GREEN);
+ }
+ else {
+ toggledComponent = Text.literal("OFF").formatted(Formatting.RED);
+ }
+
+ Text message = Text.empty()
+ .formatted(Formatting.YELLOW)
+ .append("Toggled AutoSplits ")
+ .append(toggledComponent)
+ .append("!");
+ client.player.sendMessage(message, false);
+ }
+
+}
diff --git a/zombies-timer/src/main/resources/fabric.mod.json b/zombies-timer/src/main/resources/fabric.mod.json
new file mode 100644
index 000000000..db8b9b37e
--- /dev/null
+++ b/zombies-timer/src/main/resources/fabric.mod.json
@@ -0,0 +1,35 @@
+{
+ "schemaVersion": 1,
+ "id": "zombies-autosplits",
+ "version": "${version}",
+ "name": "Phantazm Timer Mod",
+ "description": "A timer mod for Phantazm.",
+ "authors": [
+ "thamid"
+ ],
+ "contact": {
+ "issues": "https://github.com/PhantazmNetwork/PhantazmServer/issues",
+ "sources": "https://github.com/PhantazmNetwork/PhantazmServer.git"
+ },
+ "license": "AGPL-3.0-or-later",
+ "environment": "client",
+ "entrypoints": {
+ "client": [
+ "org.phantazm.zombies.autosplits.ZombiesAutoSplitsClient"
+ ],
+ "modmenu": [
+ "org.phantazm.zombies.autosplits.modmenu.ZombiesAutoSplitsModMenuApiImpl"
+ ]
+ },
+ "mixins": [
+ "zombies-autosplits.mixin.json"
+ ],
+ "depends": {
+ "fabricloader": ">=0.14.21",
+ "fabric-api": "*",
+ "minecraft": "1.20.1",
+ "java": ">=17",
+ "cloth-config": "^11.1.106",
+ "modmenu": "^7.1.0"
+ }
+}
diff --git a/zombies-timer/src/main/resources/zombies-autosplits.mixin.json b/zombies-timer/src/main/resources/zombies-autosplits.mixin.json
new file mode 100644
index 000000000..8eba4405f
--- /dev/null
+++ b/zombies-timer/src/main/resources/zombies-autosplits.mixin.json
@@ -0,0 +1,14 @@
+{
+ "required": true,
+ "minVersion": "0.8",
+ "package": "org.phantazm.zombies.autosplits.mixin",
+ "compatibilityLevel": "JAVA_17",
+ "mixins": [],
+ "client": [
+ "SoundSystemMixin"
+ ],
+ "server": [],
+ "injectors": {
+ "defaultRequire": 1
+ }
+}
diff --git a/zombies/build.gradle.kts b/zombies/build.gradle.kts
index 6973d4926..38180f812 100644
--- a/zombies/build.gradle.kts
+++ b/zombies/build.gradle.kts
@@ -5,6 +5,7 @@ plugins {
dependencies {
api(projects.phantazmCore)
+ api(projects.phantazmMessaging)
api(projects.phantazmMob)
api(projects.phantazmStats)
api(projects.phantazmZombiesMapdata)
diff --git a/zombies/src/main/java/org/phantazm/zombies/command/RoundCommand.java b/zombies/src/main/java/org/phantazm/zombies/command/RoundCommand.java
index ae6b94b14..7ccb271b4 100644
--- a/zombies/src/main/java/org/phantazm/zombies/command/RoundCommand.java
+++ b/zombies/src/main/java/org/phantazm/zombies/command/RoundCommand.java
@@ -9,7 +9,6 @@
import net.minestom.server.entity.Player;
import net.minestom.server.permission.Permission;
import org.jetbrains.annotations.NotNull;
-import org.phantazm.mob.PhantazmMob;
import org.phantazm.zombies.map.handler.RoundHandler;
import org.phantazm.zombies.scene.ZombiesScene;
import org.phantazm.zombies.stage.Stage;
@@ -59,9 +58,12 @@ public RoundCommand(@NotNull Function super UUID, Optional> scen
Stage current = transition.getCurrentStage();
if (current == null || !current.key().equals(StageKeys.IN_GAME)) {
transition.setCurrentStage(StageKeys.IN_GAME);
+ if (roundIndex != 0) {
+ handler.setCurrentRound(roundIndex);
+ }
+ } else {
+ handler.setCurrentRound(roundIndex);
}
-
- handler.setCurrentRound(roundIndex);
});
}, roundArgument);
}
diff --git a/zombies/src/main/java/org/phantazm/zombies/map/Round.java b/zombies/src/main/java/org/phantazm/zombies/map/Round.java
index 2bfe3bf47..2265f5347 100644
--- a/zombies/src/main/java/org/phantazm/zombies/map/Round.java
+++ b/zombies/src/main/java/org/phantazm/zombies/map/Round.java
@@ -1,9 +1,12 @@
package org.phantazm.zombies.map;
import net.minestom.server.MinecraftServer;
+import net.minestom.server.entity.Player;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Unmodifiable;
import org.phantazm.commons.Tickable;
+import org.phantazm.messaging.packet.PacketHandler;
+import org.phantazm.messaging.packet.c2s.RoundStartPacket;
import org.phantazm.mob.PhantazmMob;
import org.phantazm.zombies.map.action.Action;
import org.phantazm.zombies.player.ZombiesPlayer;
@@ -24,6 +27,7 @@ public class Round implements Tickable {
private final List spawnpoints;
private final Map spawnedMobs;
private final Collection extends ZombiesPlayer> zombiesPlayers;
+ private final PacketHandler packetHandler;
private boolean isActive;
private long waveStartTime;
@@ -38,7 +42,8 @@ public class Round implements Tickable {
*/
public Round(@NotNull RoundInfo roundInfo, @NotNull List waves, @NotNull List> startActions,
@NotNull List> endActions, @NotNull SpawnDistributor spawnDistributor,
- @NotNull List spawnpoints, @NotNull Collection extends ZombiesPlayer> zombiesPlayers) {
+ @NotNull List spawnpoints, @NotNull Collection extends ZombiesPlayer> zombiesPlayers,
+ @NotNull PacketHandler packetHandler) {
List waveInfo = roundInfo.waves();
if (waveInfo.isEmpty()) {
LOGGER.warn("Round {} has no waves", roundInfo);
@@ -53,6 +58,7 @@ public Round(@NotNull RoundInfo roundInfo, @NotNull List waves, @NotNull L
this.spawnedMobs = new HashMap<>();
this.spawnpoints = Objects.requireNonNull(spawnpoints, "spawnpoints");
this.zombiesPlayers = Objects.requireNonNull(zombiesPlayers, "zombiesPlayers");
+ this.packetHandler = Objects.requireNonNull(packetHandler, "packetHandler");
}
public @NotNull RoundInfo getRoundInfo() {
@@ -101,6 +107,9 @@ public void startRound(long time) {
int prevBestRound = zombiesPlayer.module().getStats().getBestRound();
zombiesPlayer.module().getStats().setBestRound(Math.max(prevBestRound, roundInfo.round()));
+ zombiesPlayer.getPlayer().ifPresent(player -> {
+ packetHandler.output(player, new RoundStartPacket());
+ });
}
for (Action action : startActions) {
diff --git a/zombies/src/main/java/org/phantazm/zombies/map/handler/BasicRoundHandler.java b/zombies/src/main/java/org/phantazm/zombies/map/handler/BasicRoundHandler.java
index f4aeb6807..51f95aecb 100644
--- a/zombies/src/main/java/org/phantazm/zombies/map/handler/BasicRoundHandler.java
+++ b/zombies/src/main/java/org/phantazm/zombies/map/handler/BasicRoundHandler.java
@@ -4,7 +4,10 @@
import org.phantazm.zombies.map.Round;
import org.phantazm.zombies.player.ZombiesPlayer;
-import java.util.*;
+import java.util.Collection;
+import java.util.List;
+import java.util.Objects;
+import java.util.Optional;
public class BasicRoundHandler implements RoundHandler {
private final Collection extends ZombiesPlayer> zombiesPlayers;
diff --git a/zombies/src/main/java/org/phantazm/zombies/map/objects/BasicMapObjectsSource.java b/zombies/src/main/java/org/phantazm/zombies/map/objects/BasicMapObjectsSource.java
index e0544dd94..05512a601 100644
--- a/zombies/src/main/java/org/phantazm/zombies/map/objects/BasicMapObjectsSource.java
+++ b/zombies/src/main/java/org/phantazm/zombies/map/objects/BasicMapObjectsSource.java
@@ -14,6 +14,7 @@
import net.kyori.adventure.key.Key;
import net.minestom.server.coordinate.Point;
import net.minestom.server.coordinate.Pos;
+import net.minestom.server.entity.Player;
import net.minestom.server.event.Event;
import net.minestom.server.event.EventNode;
import net.minestom.server.instance.Instance;
@@ -31,6 +32,7 @@
import org.phantazm.core.sound.SongLoader;
import org.phantazm.core.sound.SongPlayer;
import org.phantazm.core.tracker.BoundedTracker;
+import org.phantazm.messaging.packet.PacketHandler;
import org.phantazm.mob.MobModel;
import org.phantazm.mob.MobStore;
import org.phantazm.mob.PhantazmMob;
@@ -70,16 +72,19 @@ public class BasicMapObjectsSource implements MapObjects.Source {
private final Map mobModels;
private final ClientBlockHandlerSource clientBlockHandlerSource;
private final KeyParser keyParser;
+ private final PacketHandler packetHandler;
public BasicMapObjectsSource(@NotNull MapInfo mapInfo, @NotNull ContextManager contextManager,
@NotNull MobSpawnerSource mobSpawnerSource, @NotNull Map mobModels,
- @NotNull ClientBlockHandlerSource clientBlockHandlerSource, @NotNull KeyParser keyParser) {
+ @NotNull ClientBlockHandlerSource clientBlockHandlerSource, @NotNull KeyParser keyParser,
+ @NotNull PacketHandler packetHandler) {
this.mapInfo = Objects.requireNonNull(mapInfo, "mapInfo");
this.contextManager = Objects.requireNonNull(contextManager, "contextManager");
this.mobSpawnerSource = Objects.requireNonNull(mobSpawnerSource, "mobSpawnerSource");
this.mobModels = Objects.requireNonNull(mobModels, "mobModels");
this.clientBlockHandlerSource = Objects.requireNonNull(clientBlockHandlerSource, "clientBlockHandlerSource");
this.keyParser = Objects.requireNonNull(keyParser, "keyParser");
+ this.packetHandler = Objects.requireNonNull(packetHandler, "packetHandler");
}
@Override
@@ -264,7 +269,7 @@ private List buildRounds(List roundInfoList, List
}
rounds.add(new Round(roundInfo, waves, startActions, endActions, spawnDistributor, spawnpoints,
- zombiesPlayers.values()));
+ zombiesPlayers.values(), packetHandler));
}
return rounds;
diff --git a/zombies/src/main/java/org/phantazm/zombies/scene/ZombiesSceneProvider.java b/zombies/src/main/java/org/phantazm/zombies/scene/ZombiesSceneProvider.java
index 697877635..bca6b137b 100644
--- a/zombies/src/main/java/org/phantazm/zombies/scene/ZombiesSceneProvider.java
+++ b/zombies/src/main/java/org/phantazm/zombies/scene/ZombiesSceneProvider.java
@@ -11,6 +11,7 @@
import it.unimi.dsi.fastutil.longs.LongList;
import net.kyori.adventure.key.Key;
import net.minestom.server.coordinate.Pos;
+import net.minestom.server.entity.Player;
import net.minestom.server.event.Event;
import net.minestom.server.event.EventNode;
import net.minestom.server.event.entity.EntityDamageEvent;
@@ -35,6 +36,7 @@
import org.phantazm.core.time.AnalogTickFormatter;
import org.phantazm.core.time.PrecisionSecondTickFormatter;
import org.phantazm.core.tracker.BoundedTracker;
+import org.phantazm.messaging.packet.PacketHandler;
import org.phantazm.mob.MobModel;
import org.phantazm.mob.MobStore;
import org.phantazm.mob.trigger.EventTrigger;
@@ -94,9 +96,9 @@ public ZombiesSceneProvider(@NotNull Executor executor, int maximumScenes,
@NotNull EventNode rootNode, @NotNull MobSpawnerSource mobSpawnerSource,
@NotNull Map mobModels, @NotNull ClientBlockHandlerSource clientBlockHandlerSource,
@NotNull ContextManager contextManager, @NotNull KeyParser keyParser, @NotNull Team mobNoPushTeam,
- @NotNull Team corpseTeam, @NotNull ZombiesDatabase database, @NotNull Map powerups,
- @NotNull ZombiesPlayer.Source zombiesPlayerSource, @NotNull CorpseCreator.Source corpseCreatorSource,
- @NotNull SongLoader songLoader) {
+ @NotNull Team corpseTeam, @NotNull ZombiesDatabase database, @NotNull PacketHandler packetHandler,
+ @NotNull Map powerups, @NotNull ZombiesPlayer.Source zombiesPlayerSource,
+ @NotNull CorpseCreator.Source corpseCreatorSource, @NotNull SongLoader songLoader) {
super(executor, maximumScenes);
this.instanceSpaceFunction = Objects.requireNonNull(instanceSpaceFunction, "instanceSpaceFunction");
this.mapInfo = Objects.requireNonNull(mapInfo, "mapInfo");
@@ -113,7 +115,7 @@ public ZombiesSceneProvider(@NotNull Executor executor, int maximumScenes,
this.corpseTeam = Objects.requireNonNull(corpseTeam, "corpseTeam");
this.mapObjectSource = new BasicMapObjectsSource(mapInfo, contextManager, mobSpawnerSource, mobModels,
- clientBlockHandlerSource, keyParser);
+ clientBlockHandlerSource, keyParser, packetHandler);
this.zombiesPlayerSource = Objects.requireNonNull(zombiesPlayerSource, "zombiesPlayerSource");
this.powerupHandlerSource =
new BasicPowerupHandlerSource(powerups, contextManager, settingsInfo.powerupPickupRadius());