Instructions are immutable and cannot be modified after creation.
*
Instructions are serializable and can be sent over the network as byte arrays.
*
All X, Y, Height, Width, and other measurements used in drawing instructions are in pixels.
- *
Players render things on the screen based on their own FPS (Frames Per Second), hence the provision of timed renderings in milliseconds.
+ *
Players render things on the screen based on their own FPS (Frames Per Second), hence the provision of timed renderings in milliseconds. In addition, you can specify {@code -1} for an infinite duration.
*
SocketMC v0.1.3 introduced a permission system that allows or disallows specific types of Instructions. If permission is not given, the Instruction will silently fail.
*
*/
diff --git a/core/src/main/java/xyz/gmitch215/socketmc/retriever/RetrieverType.java b/core/src/main/java/xyz/gmitch215/socketmc/retriever/RetrieverType.java
index f9499471..855e7dec 100644
--- a/core/src/main/java/xyz/gmitch215/socketmc/retriever/RetrieverType.java
+++ b/core/src/main/java/xyz/gmitch215/socketmc/retriever/RetrieverType.java
@@ -2,7 +2,9 @@
import org.jetbrains.annotations.NotNull;
import xyz.gmitch215.socketmc.config.ModPermission;
+import xyz.gmitch215.socketmc.instruction.Instruction;
import xyz.gmitch215.socketmc.spigot.SocketPlugin;
+import xyz.gmitch215.socketmc.util.Identifier;
import xyz.gmitch215.socketmc.util.InputType;
import java.io.*;
@@ -128,6 +130,21 @@ public final class RetrieverType implements Serializable {
@RetrieverPermission(ModPermission.READ_GAME_PROPERTIES)
public static final RetrieverType HIDDEN_PLAYERS = new RetrieverType<>("hidden_players", UUID[].class);
+ /**
+ *
A retriever for all of the drawn contents on the client's screen made through SocketMC Instructions.
+ *
This currently includes the following instructions:
+ *
+ *
{@link Instruction#DRAW_TEXT}
+ *
{@link Instruction#DRAW_SHAPE}
+ *
{@link Instruction#DRAW_TEXTURE}
+ *
{@link Instruction#DRAW_BUFFER}
+ *
{@link Instruction#DRAW_CONTEXT}
+ *
{@link Instruction#DRAW_ITEMSTACK}
+ *
+ */
+ @RetrieverPermission(ModPermission.READ_GUI_PROPERTIES)
+ public static final RetrieverType DRAWN_CONTENTS = new RetrieverType<>("drawn_contents", Identifier[].class);
+
//
private final String id;
diff --git a/core/src/main/java/xyz/gmitch215/socketmc/screen/CustomScreen.java b/core/src/main/java/xyz/gmitch215/socketmc/screen/CustomScreen.java
index 212e9111..86c8c25f 100644
--- a/core/src/main/java/xyz/gmitch215/socketmc/screen/CustomScreen.java
+++ b/core/src/main/java/xyz/gmitch215/socketmc/screen/CustomScreen.java
@@ -1,13 +1,16 @@
package xyz.gmitch215.socketmc.screen;
-import xyz.gmitch215.socketmc.screen.layout.Layout;
-import xyz.gmitch215.socketmc.util.render.text.Text;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
+import org.jetbrains.annotations.Unmodifiable;
+import xyz.gmitch215.socketmc.screen.layout.Layout;
+import xyz.gmitch215.socketmc.util.render.text.Text;
import java.io.Serial;
import java.util.ArrayList;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
/**
* Represents a custom screen to be displayed on the client's screen.
@@ -23,6 +26,7 @@ public final class CustomScreen extends AbstractScreen {
private boolean closeableOnEscape = true;
private final List children = new ArrayList<>();
+ private final Map attributes = new HashMap<>();
/**
* Constructs a new screen with the given title.
@@ -174,7 +178,86 @@ public void setCloseableOnEscape(boolean closeableOnEscape) {
this.closeableOnEscape = closeableOnEscape;
}
+ /**
+ * Gets an immutable copy of the attributes of this screen.
+ * @return Map of Attributes
+ */
+ @Unmodifiable
+ @NotNull
+ public Map getAttributes() {
+ return Map.copyOf(attributes);
+ }
+
+ /**
+ * Gets an attribute from this screen.
+ * @param key the key of the attribute
+ * @return the attribute value, or null if the attribute does not exist
+ * @throws IllegalArgumentException if the key is null
+ */
+ @Nullable
+ public Object getAttribute(@NotNull String key) throws IllegalArgumentException {
+ if (key == null) throw new IllegalArgumentException("Key cannot be null");
+ return attributes.get(key);
+ }
+
+ /**
+ * Gets an attribute from this screen.
+ * @param key the key of the attribute
+ * @param def the default value if the attribute does not exist
+ * @return the attribute value, or the default value if the attribute does not exist
+ * @throws IllegalArgumentException if the key is null
+ */
+ public Object getAttribute(@NotNull String key, @Nullable Object def) throws IllegalArgumentException {
+ if (key == null) throw new IllegalArgumentException("Key cannot be null");
+ return attributes.getOrDefault(key, def);
+ }
+
+ /**
+ * Gets an attribute from this screen.
+ * @param key the key of the attribute
+ * @param type the type of the attribute
+ * @return the attribute value, or null if the attribute does not exist
+ * @param the type of the attribute
+ */
+ @Nullable
+ public T getAttribute(@NotNull String key, @NotNull Class type) {
+ if (key == null) throw new IllegalArgumentException("Key cannot be null");
+ if (type == null) throw new IllegalArgumentException("Type cannot be null");
+
+ return type.cast(attributes.get(key));
+ }
+
+ /**
+ * Gets an attribute from this screen.
+ * @param key the key of the attribute
+ * @param type the type of the attribute
+ * @param def the default value if the attribute does not exist
+ * @return the attribute value, or the default value if the attribute does not exist
+ * @param the type of the attribute
+ */
+ @Nullable
+ public T getAttribute(@NotNull String key, @NotNull Class type, @Nullable T def) {
+ if (key == null) throw new IllegalArgumentException("Key cannot be null");
+ if (type == null) throw new IllegalArgumentException("Type cannot be null");
+
+ return type.cast(attributes.getOrDefault(key, def));
+ }
+
+ /**
+ * Sets an attribute for this screen.
+ * @param key the key of the attribute
+ * @param value the value of the attribute
+ * @throws IllegalArgumentException if the key is null
+ */
+ public void setAttribute(@NotNull String key, @Nullable Object value) throws IllegalArgumentException {
+ if (key == null) throw new IllegalArgumentException("Key cannot be null");
+
+ if (value == null) attributes.remove(key);
+ else attributes.put(key, value);
+ }
+
@Override
+ @NotNull
public String toString() {
return "CustomScreen{" +
"title='" + titleJSON + '\'' +
@@ -182,6 +265,8 @@ public String toString() {
", children=" + children +
", background=" + background +
", layout=" + layout +
+ ", closeableOnEscape=" + closeableOnEscape +
+ ", attributes=" + attributes +
'}';
}
}
diff --git a/core/src/main/java/xyz/gmitch215/socketmc/util/Arm.java b/core/src/main/java/xyz/gmitch215/socketmc/util/Arm.java
new file mode 100644
index 00000000..c4c0e896
--- /dev/null
+++ b/core/src/main/java/xyz/gmitch215/socketmc/util/Arm.java
@@ -0,0 +1,41 @@
+package xyz.gmitch215.socketmc.util;
+
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * Represents a player's arm.
+ */
+public enum Arm {
+
+ /**
+ * Represents the left arm.
+ */
+ LEFT,
+
+ /**
+ * Represents the right arm.
+ */
+ RIGHT;
+
+ /**
+ * Returns the opposite arm.
+ * @return the opposite arm
+ */
+ @NotNull
+ public Arm opposite() {
+ return this == LEFT ? RIGHT : LEFT;
+ }
+
+ /**
+ * Gets the Arm by its ordinal.
+ * @param ordinal
+ * @return Arm by ordinal
+ */
+ @NotNull
+ public static Arm byOrdinal(int ordinal) {
+ return values()[ordinal];
+ }
+
+
+
+}
diff --git a/core/src/main/java/xyz/gmitch215/socketmc/util/Identifier.java b/core/src/main/java/xyz/gmitch215/socketmc/util/Identifier.java
index 4a2a115c..1568b3e8 100644
--- a/core/src/main/java/xyz/gmitch215/socketmc/util/Identifier.java
+++ b/core/src/main/java/xyz/gmitch215/socketmc/util/Identifier.java
@@ -5,6 +5,7 @@
import java.io.Serial;
import java.io.Serializable;
import java.util.Objects;
+import java.util.UUID;
/**
*
Represents a Namespaced Identifier.
@@ -73,4 +74,24 @@ public static Identifier minecraft(@NotNull String path) {
return new Identifier("minecraft", path);
}
+ /**
+ * Creates a new Identifier with the namespace "socketmc".
+ * @param path The path of the Identifier.
+ * @return The new Identifier.
+ */
+ @NotNull
+ public static Identifier socketmc(@NotNull String path) {
+ return new Identifier("socketmc", path);
+ }
+
+ /**
+ * Creates a new random Identifier with the namespace "socketmc" and a random UUID as its path.
+ * @return The new random Identifier.
+ */
+ @NotNull
+ public static Identifier random() {
+ UUID uuid = UUID.randomUUID();
+ return Identifier.socketmc(uuid.toString());
+ }
+
}
diff --git a/core/src/main/java/xyz/gmitch215/socketmc/util/LifecycleMap.java b/core/src/main/java/xyz/gmitch215/socketmc/util/LifecycleMap.java
index a770596c..b77a2ff7 100644
--- a/core/src/main/java/xyz/gmitch215/socketmc/util/LifecycleMap.java
+++ b/core/src/main/java/xyz/gmitch215/socketmc/util/LifecycleMap.java
@@ -7,6 +7,7 @@
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
+import java.util.Set;
import java.util.concurrent.TimeUnit;
/**
@@ -15,6 +16,7 @@
*/
public class LifecycleMap implements Iterable {
+ private final Map identifiers = new HashMap<>();
private final Map origin = new HashMap<>();
private final Map duration = new HashMap<>();
@@ -38,9 +40,11 @@ public LifecycleMap(@Nullable LifecycleMap map) {
* Runs the map, removing any expired values.
*/
public void run() {
- for (T key : origin.keySet()) {
+ Iterator it = origin.keySet().iterator();
+ while (it.hasNext()) {
+ T key = it.next();
if (getRemainingTime(key) <= 0) {
- origin.remove(key);
+ it.remove();
duration.remove(key);
}
}
@@ -48,54 +52,160 @@ public void run() {
/**
* Associates the specified value with the specified key in this map.
+ * @param id The identifier for the key
* @param key The key with which the specified value is to be associated
* @param startMillis The start time of the value, in milliseconds
- * @param durationMillis The duration of the value, in milliseconds
+ * @param durationMillis The duration of the value, in milliseconds (use {@code -1} for infinite)
+ * @return The identifier of the key
*/
- public void put(@NotNull T key, long startMillis, long durationMillis) {
+ @NotNull
+ public Identifier put(@NotNull Identifier id, @NotNull T key, long startMillis, long durationMillis) {
+ if (id == null) throw new IllegalArgumentException("Identifier cannot be null");
+ if (key == null) throw new IllegalArgumentException("Key cannot be null");
+ if (startMillis < 0) throw new IllegalArgumentException("Start time cannot be negative");
+ if (durationMillis < -1) throw new IllegalArgumentException("Duration cannot be less than -1");
+
+ identifiers.put(key, id);
origin.put(key, startMillis);
duration.put(key, durationMillis);
+
+ return id;
+ }
+
+ /**
+ * Associates the specified value with the specified key in this map.
+ * @param key The key with which the specified value is to be associated
+ * @param startMillis The start time of the value, in milliseconds
+ * @param durationMillis The duration of the value, in milliseconds (use {@code -1} for infinite)
+ * @return The identifier of the key
+ */
+ @NotNull
+ public Identifier put(@NotNull T key, long startMillis, long durationMillis) {
+ return put(Identifier.random(), key, startMillis, durationMillis);
}
/**
* Stores the specified key in this map for the specified duration.
+ * @param id The identifier for the key
* @param key The key to store
- * @param millis The duration to store the key for, in milliseconds
+ * @param millis The duration to store the key for, in milliseconds (use {@code -1} for infinite)
+ * @return The identifier of the key
*/
- public void store(@NotNull T key, long millis) {
- put(key, System.currentTimeMillis(), millis);
+ @NotNull
+ public Identifier store(@NotNull Identifier id, @NotNull T key, long millis) {
+ return put(id, key, System.currentTimeMillis(), millis);
}
/**
- * Returns the value to which the specified key is mapped, or null if this map contains no mapping for the key.
+ * Stores the specified key in this map for the specified duration.
+ * @param key The key to store
+ * @param millis The duration to store the key for, in milliseconds (use {@code -1} for infinite)
+ * @return The identifier of the key
+ */
+ @NotNull
+ public Identifier store(@NotNull T key, long millis) {
+ return store(Identifier.random(), key, millis);
+ }
+
+ /**
+ * Stores the specified key in this map for the specified duration.
+ * @param id The identifier for the key
* @param key The key whose associated value is to be returned
* @param time The current time, in milliseconds
* @param unit The time unit of the duration
+ * @return The identifier for the key
*/
- public void store(@NotNull T key, long time, @NotNull TimeUnit unit) {
+ @NotNull
+ public Identifier store(@NotNull Identifier id, @NotNull T key, long time, @NotNull TimeUnit unit) {
if (unit == null) throw new IllegalArgumentException("TimeUnit cannot be null");
- put(key, System.currentTimeMillis(), unit.toMillis(time));
+ return put(id, key, System.currentTimeMillis(), unit.toMillis(time));
+ }
+
+ /**
+ * Stores the specified key in this map for the specified duration.
+ * @param key The key whose associated value is to be returned
+ * @param time The current time, in milliseconds
+ * @param unit The time unit of the duration
+ * @return The identifier for the key
+ */
+ @NotNull
+ public Identifier store(@NotNull T key, long time, @NotNull TimeUnit unit) {
+ return store(Identifier.random(), key, time, unit);
}
/**
- * Returns the value to which the specified key is mapped, or null if this map contains no mapping for the key.
+ * Stores the specified key in this map for the specified duration.
+ * @param id The identifier for the key
* @param key The key whose associated value is to be returned
* @param duration The duration to store the key for
+ * @return The identifier for the key
*/
- public void store(@NotNull T key, @NotNull Duration duration) {
+ @NotNull
+ public Identifier store(@NotNull Identifier id, @NotNull T key, @NotNull Duration duration) {
if (duration == null) throw new IllegalArgumentException("TimeUnit cannot be null");
- put(key, System.currentTimeMillis(), duration.toMillis());
+ return put(id, key, System.currentTimeMillis(), duration.toMillis());
+ }
+
+ /**
+ * Stores the specified key in this map for the specified duration.
+ * @param key The key whose associated value is to be returned
+ * @param duration The duration to store the key for
+ * @return The identifier for the key
+ */
+ @NotNull
+ public Identifier store(@NotNull T key, @NotNull Duration duration) {
+ return store(Identifier.random(), key, duration);
+ }
+
+ /**
+ * Stores the specified key in this map for an infinite duration.
+ * @param id The identifier for the key
+ * @param key The key whose associated value is to be returned
+ * @return The identifier for the key
+ */
+ @NotNull
+ public Identifier storeInfinite(@NotNull Identifier id, @NotNull T key) {
+ return store(id, key, -1);
}
/**
- * Returns the value to which the specified key is mapped, or null if this map contains no mapping for the key.
+ * Stores the specified key in this map for an infinite duration.
+ * @param key The key whose associated value is to be returned
+ * @return The identifier for the key
+ */
+ @NotNull
+ public Identifier storeInfinite(@NotNull T key) {
+ return store(key, -1);
+ }
+
+ /**
+ * Removes the mapping for the specified key from this map if present.
* @param key The key whose associated value is to be returned
*/
public void remove(@NotNull T key) {
+ identifiers.remove(key);
origin.remove(key);
duration.remove(key);
}
+ /**
+ * Removes the mapping for the specified identifier from this map if present.
+ * @param id The identifier whose associated value is to be removed
+ * @return The key that was removed, or null if the identifier was not present
+ */
+ @Nullable
+ public T remove(@NotNull Identifier id) {
+ if (id == null) throw new IllegalArgumentException("Identifier cannot be null");
+ T key = getContents().get(id);
+
+ if (key != null) {
+ remove(key);
+ return key;
+ }
+
+ return null;
+ }
+
/**
* Returns the start time of the specified key.
* @param key The key whose start time is to be returned
@@ -117,13 +227,57 @@ public long getDuration(@NotNull T key) {
/**
* Returns the remaining time of the specified key.
* @param key The key whose remaining time is to be returned
- * @return The remaining time of the key, in milliseconds, or -1 if the key is not present
+ * @return The remaining time of the key (in milliseconds), {@code -1} if the key is not present, or {@link Long#MAX_VALUE} if the key is infinite
*/
public long getRemainingTime(@NotNull T key) {
if (!origin.containsKey(key)) return -1;
+ if (duration.get(key) == -1) return Long.MAX_VALUE;
+
return (origin.get(key) + duration.get(key)) - System.currentTimeMillis();
}
+ /**
+ * Returns true if the specified key has an infinite duration.
+ * @param key The key whose duration is to be checked
+ * @return True if the key has an infinite duration
+ */
+ public boolean isInfinite(@NotNull T key) {
+ return getDuration(key) == -1;
+ }
+
+ /**
+ * Returns the identifier of the specified key.
+ * @param key The key whose identifier is to be returned
+ * @return The identifier of the key
+ */
+ @NotNull
+ public Identifier getIdentifier(@NotNull T key) {
+ return identifiers.get(key);
+ }
+
+ /**
+ * Gets an immutable copy of the contents of the LifecycleMap as a map of identifiers to keys.
+ * @return The contents of the LifecycleMap
+ */
+ @NotNull
+ public Map getContents() {
+ Map contents = new HashMap<>();
+
+ for (T key : origin.keySet())
+ contents.put(identifiers.get(key), key);
+
+ return Map.copyOf(contents);
+ }
+
+ /**
+ * Fetches an immutable set of the identifiers in the map.
+ * @return Set of identifiers for their keys
+ */
+ @NotNull
+ public Set getIdentifiers() {
+ return Set.copyOf(identifiers.values());
+ }
+
/**
* Gets the size of the map.
* @return The size of the map
@@ -141,9 +295,34 @@ public boolean containsKey(@NotNull T key) {
return origin.containsKey(key);
}
+ /**
+ * Returns true if this map contains a mapping for the specified identifier.
+ * @param id The identifier whose presence in this map is to be tested
+ * @return True if this map contains a mapping for the specified identifier
+ */
+ public boolean containsIdentifier(@NotNull Identifier id) {
+ return identifiers.containsValue(id);
+ }
+
@NotNull
@Override
public Iterator iterator() {
return origin.keySet().iterator();
}
+
+ /**
+ * Removes a specific key from multiple LifecycleMaps.
+ * @param id The identifier of the key to remove
+ * @param maps The LifecycleMaps to remove the key from
+ * @throws IllegalArgumentException if the maps are null
+ */
+ public static void removeIn(@NotNull Identifier id, @NotNull LifecycleMap>... maps) {
+ if (id == null) throw new IllegalArgumentException("Identifier cannot be null");
+ if (maps == null) throw new IllegalArgumentException("Maps cannot be null");
+
+ for (LifecycleMap> map : maps) {
+ if (map.remove(id) != null)
+ return;
+ }
+ }
}
diff --git a/core/src/main/java/xyz/gmitch215/socketmc/util/option/AttackIndicator.java b/core/src/main/java/xyz/gmitch215/socketmc/util/option/AttackIndicator.java
new file mode 100644
index 00000000..276ab8cb
--- /dev/null
+++ b/core/src/main/java/xyz/gmitch215/socketmc/util/option/AttackIndicator.java
@@ -0,0 +1,37 @@
+package xyz.gmitch215.socketmc.util.option;
+
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * Represents the status of the attack indicator.
+ */
+public enum AttackIndicator {
+
+ /**
+ * The attack indicator is disabled.
+ */
+ OFF,
+
+ /**
+ * The attack indicator is displayed in the crosshair.
+ */
+ CROSSHAIR,
+
+ /**
+ * The attack indicator is displayed in the hotbar.
+ */
+ HOTBAR
+
+ ;
+
+ /**
+ * Gets the AttackIndicator by its ordinal.
+ * @param ordinal
+ * @return AttackIndicator by ordinal
+ */
+ @NotNull
+ public static AttackIndicator byOrdinal(int ordinal) {
+ return values()[ordinal];
+ }
+
+}
diff --git a/core/src/main/java/xyz/gmitch215/socketmc/util/option/ChatVisibility.java b/core/src/main/java/xyz/gmitch215/socketmc/util/option/ChatVisibility.java
new file mode 100644
index 00000000..38564585
--- /dev/null
+++ b/core/src/main/java/xyz/gmitch215/socketmc/util/option/ChatVisibility.java
@@ -0,0 +1,37 @@
+package xyz.gmitch215.socketmc.util.option;
+
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * Represents the visibility of chat messages.
+ */
+public enum ChatVisibility {
+
+ /**
+ * All chat messages are visible.
+ */
+ FULL,
+
+ /**
+ * Only chat messages from players that are in the same team are visible.
+ */
+ SYSTEM,
+
+ /**
+ * No chat messages are visible.
+ */
+ HIDDEN
+
+ ;
+
+ /**
+ * Gets the ChatVisibility by its ordinal.
+ * @param ordinal
+ * @return ChatVisibility by ordinal
+ */
+ @NotNull
+ public static ChatVisibility byOrdinal(int ordinal) {
+ return values()[ordinal];
+ }
+
+}
diff --git a/core/src/main/java/xyz/gmitch215/socketmc/util/option/ChunkUpdatePriority.java b/core/src/main/java/xyz/gmitch215/socketmc/util/option/ChunkUpdatePriority.java
new file mode 100644
index 00000000..0d4ae946
--- /dev/null
+++ b/core/src/main/java/xyz/gmitch215/socketmc/util/option/ChunkUpdatePriority.java
@@ -0,0 +1,37 @@
+package xyz.gmitch215.socketmc.util.option;
+
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * Represents the priority for chunk updates.
+ */
+public enum ChunkUpdatePriority {
+
+ /**
+ * Chunk updates are not prioritized.
+ */
+ NONE,
+
+ /**
+ * Chunk updates are prioritized if a player is nearby.
+ */
+ PLAYER_AFFECTED,
+
+ /**
+ * Chunk updates are prioritized if the client is rendering the chunk.
+ */
+ NEARBY
+
+ ;
+
+ /**
+ * Gets the ChunkUpdatePriority by its ordinal.
+ * @param ordinal The ordinal
+ * @return ChunkUpdatePriority by ordinal
+ */
+ @NotNull
+ public static ChunkUpdatePriority byOrdinal(int ordinal) {
+ return values()[ordinal];
+ }
+
+}
diff --git a/core/src/main/java/xyz/gmitch215/socketmc/util/option/CloudRendering.java b/core/src/main/java/xyz/gmitch215/socketmc/util/option/CloudRendering.java
new file mode 100644
index 00000000..bbb56318
--- /dev/null
+++ b/core/src/main/java/xyz/gmitch215/socketmc/util/option/CloudRendering.java
@@ -0,0 +1,37 @@
+package xyz.gmitch215.socketmc.util.option;
+
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * Represents the status of cloud rendering.
+ */
+public enum CloudRendering {
+
+ /**
+ * Clouds are not rendered.
+ */
+ OFF,
+
+ /**
+ * Clouds are rendered in performance mode.
+ */
+ FAST,
+
+ /**
+ * Clouds are rendered in quality mode.
+ */
+ FANCY
+
+ ;
+
+ /**
+ * Gets the CloudRendering by its ordinal.
+ * @param ordinal
+ * @return CloudRendering by ordinal
+ */
+ @NotNull
+ public static CloudRendering byOrdinal(int ordinal) {
+ return values()[ordinal];
+ }
+
+}
diff --git a/core/src/main/java/xyz/gmitch215/socketmc/util/option/GraphicsQuality.java b/core/src/main/java/xyz/gmitch215/socketmc/util/option/GraphicsQuality.java
new file mode 100644
index 00000000..44751f21
--- /dev/null
+++ b/core/src/main/java/xyz/gmitch215/socketmc/util/option/GraphicsQuality.java
@@ -0,0 +1,37 @@
+package xyz.gmitch215.socketmc.util.option;
+
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * Represents the different graphics rendering options.
+ */
+public enum GraphicsQuality {
+
+ /**
+ * Graphics are rendered in the fastest way possible.
+ */
+ FAST,
+
+ /**
+ * Graphics are rendered in a medium quality way.
+ */
+ FANCY,
+
+ /**
+ * Graphics are rendered in the highest quality possible.
+ */
+ FABULOUS
+
+ ;
+
+ /**
+ * Gets the GraphicsRendering by its ordinal.
+ * @param ordinal
+ * @return GraphicsRendering by ordinal
+ */
+ @NotNull
+ public static GraphicsQuality byOrdinal(int ordinal) {
+ return values()[ordinal];
+ }
+
+}
diff --git a/core/src/main/java/xyz/gmitch215/socketmc/util/option/NarratorStatus.java b/core/src/main/java/xyz/gmitch215/socketmc/util/option/NarratorStatus.java
new file mode 100644
index 00000000..8b640711
--- /dev/null
+++ b/core/src/main/java/xyz/gmitch215/socketmc/util/option/NarratorStatus.java
@@ -0,0 +1,39 @@
+package xyz.gmitch215.socketmc.util.option;
+
+/**
+ * Represents the status of the narrator.
+ */
+public enum NarratorStatus {
+
+ /**
+ * The narrator is off.
+ */
+ OFF,
+
+ /**
+ * The narrator speaks on everything.
+ */
+ ALL,
+
+ /**
+ * The narrator speaks on chat messages.
+ */
+ CHAT,
+
+ /**
+ * The narrator speaks on system messages.
+ */
+ SYSTEM
+
+ ;
+
+ /**
+ * Gets the NarratorStatus by its ordinal.
+ * @param ordinal the ordinal
+ * @return NarratorStatus by ordinal
+ */
+ public static NarratorStatus byOrdinal(int ordinal) {
+ return values()[ordinal];
+ }
+
+}
diff --git a/core/src/main/java/xyz/gmitch215/socketmc/util/option/ParticleRendering.java b/core/src/main/java/xyz/gmitch215/socketmc/util/option/ParticleRendering.java
new file mode 100644
index 00000000..0c5701fc
--- /dev/null
+++ b/core/src/main/java/xyz/gmitch215/socketmc/util/option/ParticleRendering.java
@@ -0,0 +1,37 @@
+package xyz.gmitch215.socketmc.util.option;
+
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * Represents the different ways particles can be rendered.
+ */
+public enum ParticleRendering {
+
+ /**
+ * Particles are rendered in full.
+ */
+ ALL,
+
+ /**
+ * Particles are rendered in a reduced form.
+ */
+ DECREASED,
+
+ /**
+ * Particles are not rendered at all.
+ */
+ MINIMAL
+
+ ;
+
+ /**
+ * Gets the ParticleRendering by its ordinal.
+ * @param ordinal
+ * @return ParticleRendering by ordinal
+ */
+ @NotNull
+ public static ParticleRendering byOrdinal(int ordinal) {
+ return values()[ordinal];
+ }
+
+}
diff --git a/core/src/main/javadoc/xyz/gmitch215/socketmc/util/option/package-info.java b/core/src/main/javadoc/xyz/gmitch215/socketmc/util/option/package-info.java
new file mode 100644
index 00000000..5010bfe4
--- /dev/null
+++ b/core/src/main/javadoc/xyz/gmitch215/socketmc/util/option/package-info.java
@@ -0,0 +1,4 @@
+/**
+ * Package containing different client options that can be set in the game.
+ */
+package xyz.gmitch215.socketmc.util.option;
\ No newline at end of file
diff --git a/core/src/test/java/xyz/gmitch215/socketmc/util/TestLifecycleMap.java b/core/src/test/java/xyz/gmitch215/socketmc/util/TestLifecycleMap.java
new file mode 100644
index 00000000..ed14402e
--- /dev/null
+++ b/core/src/test/java/xyz/gmitch215/socketmc/util/TestLifecycleMap.java
@@ -0,0 +1,64 @@
+package xyz.gmitch215.socketmc.util;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+
+import java.util.function.Function;
+
+public class TestLifecycleMap {
+
+ @Test
+ @DisplayName("Test LifecycleMap#put")
+ public void testPut() {
+ LifecycleMap map = new LifecycleMap<>();
+
+ map.put(1, 0, 1000);
+ map.put(2, 1000, 1000);
+ map.put(3, 2000, 1000);
+
+ Assertions.assertEquals(3, map.size());
+ for (int i = 1; i <= 3; i++) {
+ Assertions.assertTrue(map.containsKey(i));
+ Assertions.assertEquals(1000, map.getDuration(i));
+ }
+ }
+
+ @Test
+ @DisplayName("Test LifecycleMap#run")
+ public void testRun() {
+ LifecycleMap map = new LifecycleMap<>();
+
+ map.put(1, 0, 1000);
+ map.put(2, 1000, 1000);
+ map.put(3, 2000, 1000);
+
+ Assertions.assertEquals(3, map.size());
+
+ while (map.size() > 0) {
+ map.run();
+ }
+
+ Assertions.assertEquals(0, map.size());
+ }
+
+ @Test
+ @DisplayName("Test LifecycleMap#store")
+ public void testStore() {
+ LifecycleMap> map = new LifecycleMap<>();
+
+ map.store(i -> i + 1, 500);
+ map.store(i -> i * 2, 1000);
+ map.store(i -> i * i, 1500);
+
+ Assertions.assertEquals(3, map.size());
+
+ while (map.size() > 0) {
+ map.run();
+ map.forEach(f -> f.apply(0));
+ }
+
+ Assertions.assertEquals(0, map.size());
+ }
+
+}
diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar
index 2c352119..a4b76b95 100644
Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index 9355b415..df97d72b 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-8.10-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
diff --git a/mod/fabric/build.gradle.kts b/mod/fabric/build.gradle.kts
index a13e38a8..a14fac44 100644
--- a/mod/fabric/build.gradle.kts
+++ b/mod/fabric/build.gradle.kts
@@ -9,7 +9,7 @@ plugins {
description = "Fabric Mod for SocketMC Client-side Implementation"
-val minecraft = project.ext["minecraft_version"].toString()
+val mc = project.ext["minecraft_version"].toString()
val parchment = project.ext["parchment"].toString()
val fabric = "0.102.0"
@@ -18,10 +18,10 @@ dependencies {
api(project(":socketmc-core"))
api(project(":socketmc-shared"))
- minecraft("com.mojang:minecraft:$minecraft")
+ minecraft("com.mojang:minecraft:$mc")
mappings(loom.layered {
officialMojangMappings()
- parchment("org.parchmentmc.data:parchment-$minecraft:$parchment@zip")
+ parchment("org.parchmentmc.data:parchment-$mc:$parchment@zip")
})
modImplementation("net.fabricmc:fabric-loader:0.15.11")
@@ -35,7 +35,7 @@ dependencies {
"fabric-screen-api-v1",
"fabric-key-binding-api-v1"
).forEach {
- modImplementation(fabricApi.module(it, "$fabric+$minecraft"))
+ modImplementation(fabricApi.module(it, "$fabric+$mc"))
}
annotationProcessor("org.spongepowered:mixin:0.8.6:processor")
@@ -92,13 +92,14 @@ modrinth {
versionType.set(project.ext["version_type"].toString())
uploadFile.set(tasks.remapJar)
- gameVersions.add(project.ext["minecraft_version"].toString())
changelog.set(project.ext["changelog"].toString())
+ gameVersions.add(mc)
+ gameVersions.addAll((project.ext["similar_versions"] as List<*>).map { it.toString() })
+
loaders.addAll(listOf("fabric", "quilt"))
- dependencies {
- required.project("fabric-api")
- }
+
+ required.project("fabric-api")
syncBodyFrom.set(rootProject.file("README.md").bufferedReader().use { it.readText() })
}
\ No newline at end of file
diff --git a/mod/fabric/src/main/java/xyz/gmitch215/socketmc/fabric/FabricRendering.java b/mod/fabric/src/main/java/xyz/gmitch215/socketmc/fabric/FabricRendering.java
new file mode 100644
index 00000000..0db75c69
--- /dev/null
+++ b/mod/fabric/src/main/java/xyz/gmitch215/socketmc/fabric/FabricRendering.java
@@ -0,0 +1,19 @@
+package xyz.gmitch215.socketmc.fabric;
+
+import com.mojang.blaze3d.platform.Window;
+
+import static xyz.gmitch215.socketmc.fabric.FabricSocketMC.minecraft;
+import static xyz.gmitch215.socketmc.util.RenderingProperties.REFRESH_RATE;
+
+public final class FabricRendering {
+
+ private FabricRendering() {}
+
+ public static void frameTick() {
+ // Set Rendering Properties
+ Window window = minecraft.getWindow();
+
+ REFRESH_RATE.set(window.getRefreshRate());
+ }
+
+}
diff --git a/mod/fabric/src/main/java/xyz/gmitch215/socketmc/fabric/FabricRetriever.java b/mod/fabric/src/main/java/xyz/gmitch215/socketmc/fabric/FabricRetriever.java
index bbb464aa..8e5a8276 100644
--- a/mod/fabric/src/main/java/xyz/gmitch215/socketmc/fabric/FabricRetriever.java
+++ b/mod/fabric/src/main/java/xyz/gmitch215/socketmc/fabric/FabricRetriever.java
@@ -3,15 +3,19 @@
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFuture;
import net.minecraft.network.FriendlyByteBuf;
+import xyz.gmitch215.socketmc.fabric.machines.*;
import xyz.gmitch215.socketmc.retriever.ClientProperty;
import xyz.gmitch215.socketmc.retriever.Retriever;
import xyz.gmitch215.socketmc.retriever.RetrieverType;
import xyz.gmitch215.socketmc.retriever.Window;
+import xyz.gmitch215.socketmc.util.Identifier;
import xyz.gmitch215.socketmc.util.InputType;
+import xyz.gmitch215.socketmc.util.RenderingProperties;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
+import java.util.List;
import java.util.Set;
import java.util.UUID;
import java.util.stream.Collectors;
@@ -44,7 +48,7 @@ private FabricRetriever() {}
window.getGuiScale(),
window.getFramerateLimit(),
com.mojang.blaze3d.platform.Window.getPlatform(),
- window.getRefreshRate()
+ RenderingProperties.REFRESH_RATE.get()
);
}),
create(RetrieverType.PAUSED, minecraft::isPaused),
@@ -58,7 +62,15 @@ private FabricRetriever() {}
case MOUSE -> InputType.MOUSE;
default -> InputType.NONE;
}),
- create(RetrieverType.COMMAND_HISTORY, () -> minecraft.commandHistory().history().toArray(new String[0]))
+ create(RetrieverType.COMMAND_HISTORY, () -> minecraft.commandHistory().history().toArray(new String[0])),
+ create(RetrieverType.DRAWN_CONTENTS, () -> Stream.of(
+ DrawTextMachine.lifecycle.getIdentifiers(),
+ DrawShapeMachine.lifecycle.getIdentifiers(),
+ DrawTextureMachine.lifecycle.getIdentifiers(),
+ DrawBufferMachine.lifecycle.getIdentifiers(),
+ DrawContextMachine.lifecycle.getIdentifiers(),
+ DrawItemStackMachine.lifecycle.getIdentifiers()
+ ).flatMap(Set::stream).distinct().toArray(Identifier[]::new))
)
).collect(Collectors.toSet());
diff --git a/mod/fabric/src/main/java/xyz/gmitch215/socketmc/fabric/FabricSocketMC.java b/mod/fabric/src/main/java/xyz/gmitch215/socketmc/fabric/FabricSocketMC.java
index 1c6a0268..713fa395 100644
--- a/mod/fabric/src/main/java/xyz/gmitch215/socketmc/fabric/FabricSocketMC.java
+++ b/mod/fabric/src/main/java/xyz/gmitch215/socketmc/fabric/FabricSocketMC.java
@@ -41,6 +41,8 @@ public void onInitializeClient() {
// Events - Machines
HudRenderCallback.EVENT.register((graphics, delta) -> {
+ FabricRendering.frameTick();
+
DrawTextMachine.frameTick(graphics, delta);
DrawShapeMachine.frameTick(graphics, delta);
DrawBufferMachine.frameTick(graphics, delta);
diff --git a/mod/fabric/src/main/java/xyz/gmitch215/socketmc/fabric/machines/DrawBufferMachine.java b/mod/fabric/src/main/java/xyz/gmitch215/socketmc/fabric/machines/DrawBufferMachine.java
index e705dec8..7608fab8 100644
--- a/mod/fabric/src/main/java/xyz/gmitch215/socketmc/fabric/machines/DrawBufferMachine.java
+++ b/mod/fabric/src/main/java/xyz/gmitch215/socketmc/fabric/machines/DrawBufferMachine.java
@@ -21,7 +21,7 @@ public final class DrawBufferMachine implements Machine {
private DrawBufferMachine() {}
- private static final LifecycleMap> lifecycle = new LifecycleMap<>();
+ public static final LifecycleMap> lifecycle = new LifecycleMap<>();
public static void frameTick(GuiGraphics graphics, DeltaTracker delta) {
lifecycle.run();
diff --git a/mod/fabric/src/main/java/xyz/gmitch215/socketmc/fabric/machines/DrawContextMachine.java b/mod/fabric/src/main/java/xyz/gmitch215/socketmc/fabric/machines/DrawContextMachine.java
index a0ff3c33..bd1b7463 100644
--- a/mod/fabric/src/main/java/xyz/gmitch215/socketmc/fabric/machines/DrawContextMachine.java
+++ b/mod/fabric/src/main/java/xyz/gmitch215/socketmc/fabric/machines/DrawContextMachine.java
@@ -43,7 +43,7 @@ public final class DrawContextMachine implements Machine {
private DrawContextMachine() {}
- private static final LifecycleMap> lifecycle = new LifecycleMap<>();
+ public static final LifecycleMap> lifecycle = new LifecycleMap<>();
public static void frameTick(GuiGraphics graphics, DeltaTracker delta) {
lifecycle.run();
diff --git a/mod/fabric/src/main/java/xyz/gmitch215/socketmc/fabric/machines/DrawItemStackMachine.java b/mod/fabric/src/main/java/xyz/gmitch215/socketmc/fabric/machines/DrawItemStackMachine.java
index 69d2907e..77cad292 100644
--- a/mod/fabric/src/main/java/xyz/gmitch215/socketmc/fabric/machines/DrawItemStackMachine.java
+++ b/mod/fabric/src/main/java/xyz/gmitch215/socketmc/fabric/machines/DrawItemStackMachine.java
@@ -20,7 +20,7 @@ public final class DrawItemStackMachine implements Machine {
private DrawItemStackMachine() {}
- private static final LifecycleMap> lifecycle = new LifecycleMap<>();
+ public static final LifecycleMap> lifecycle = new LifecycleMap<>();
public static void frameTick(GuiGraphics graphics, DeltaTracker delta) {
lifecycle.run();
diff --git a/mod/fabric/src/main/java/xyz/gmitch215/socketmc/fabric/machines/DrawShapeMachine.java b/mod/fabric/src/main/java/xyz/gmitch215/socketmc/fabric/machines/DrawShapeMachine.java
index 379d91be..31ca488e 100644
--- a/mod/fabric/src/main/java/xyz/gmitch215/socketmc/fabric/machines/DrawShapeMachine.java
+++ b/mod/fabric/src/main/java/xyz/gmitch215/socketmc/fabric/machines/DrawShapeMachine.java
@@ -17,7 +17,7 @@ public final class DrawShapeMachine implements Machine {
private DrawShapeMachine() {}
- private static final LifecycleMap> lifecycle = new LifecycleMap<>();
+ public static final LifecycleMap> lifecycle = new LifecycleMap<>();
public static void frameTick(GuiGraphics graphics, DeltaTracker delta) {
lifecycle.run();
diff --git a/mod/fabric/src/main/java/xyz/gmitch215/socketmc/fabric/machines/DrawTextMachine.java b/mod/fabric/src/main/java/xyz/gmitch215/socketmc/fabric/machines/DrawTextMachine.java
index b51d510e..1ad5995d 100644
--- a/mod/fabric/src/main/java/xyz/gmitch215/socketmc/fabric/machines/DrawTextMachine.java
+++ b/mod/fabric/src/main/java/xyz/gmitch215/socketmc/fabric/machines/DrawTextMachine.java
@@ -20,7 +20,7 @@ public final class DrawTextMachine implements Machine {
private DrawTextMachine() {}
- private static final LifecycleMap> lifecycle = new LifecycleMap<>();
+ public static final LifecycleMap> lifecycle = new LifecycleMap<>();
public static void frameTick(GuiGraphics graphics, DeltaTracker delta) {
lifecycle.run();
diff --git a/mod/fabric/src/main/java/xyz/gmitch215/socketmc/fabric/machines/DrawTextureMachine.java b/mod/fabric/src/main/java/xyz/gmitch215/socketmc/fabric/machines/DrawTextureMachine.java
index eed57d23..99c5ed6e 100644
--- a/mod/fabric/src/main/java/xyz/gmitch215/socketmc/fabric/machines/DrawTextureMachine.java
+++ b/mod/fabric/src/main/java/xyz/gmitch215/socketmc/fabric/machines/DrawTextureMachine.java
@@ -19,7 +19,7 @@ public final class DrawTextureMachine implements Machine {
private DrawTextureMachine() {}
- private static final LifecycleMap> lifecycle = new LifecycleMap<>();
+ public static final LifecycleMap> lifecycle = new LifecycleMap<>();
public static void frameTick(GuiGraphics graphics, DeltaTracker delta) {
lifecycle.run();
diff --git a/mod/fabric/src/main/java/xyz/gmitch215/socketmc/fabric/mixin/events/PlayerChangeOptionEvent.java b/mod/fabric/src/main/java/xyz/gmitch215/socketmc/fabric/mixin/events/PlayerChangeOptionEvent.java
new file mode 100644
index 00000000..ba2d2948
--- /dev/null
+++ b/mod/fabric/src/main/java/xyz/gmitch215/socketmc/fabric/mixin/events/PlayerChangeOptionEvent.java
@@ -0,0 +1,73 @@
+package xyz.gmitch215.socketmc.fabric.mixin.events;
+
+import net.minecraft.client.*;
+import net.minecraft.client.NarratorStatus;
+import net.minecraft.network.chat.Component;
+import net.minecraft.network.chat.contents.TranslatableContents;
+import net.minecraft.world.entity.HumanoidArm;
+import net.minecraft.world.entity.player.ChatVisiblity;
+import org.spongepowered.asm.mixin.Final;
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.Shadow;
+import org.spongepowered.asm.mixin.Unique;
+import org.spongepowered.asm.mixin.injection.At;
+import org.spongepowered.asm.mixin.injection.Inject;
+import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
+import xyz.gmitch215.socketmc.fabric.FabricSocketMC;
+import xyz.gmitch215.socketmc.util.Arm;
+import xyz.gmitch215.socketmc.util.option.*;
+
+import java.util.Map;
+import java.util.function.Function;
+
+@Mixin(OptionInstance.class)
+public class PlayerChangeOptionEvent {
+
+ @Final
+ @Shadow
+ Component caption;
+
+ @Shadow
+ Object value;
+
+ @Final
+ @Shadow
+ Function