getFiles() {
return files;
}
@@ -54,6 +54,17 @@ public class EditorManager {
editorFileView.setEditorFile(files.getFirst());
editorFileView.addToEditorScreen(textEditorScreen);
+ /*
+ files = new ArrayList<>();
+
+ // Add example file
+ EditorFile editorFile = new EditorFile("main.lua", "");
+ files.add(editorFile);
+
+ */
+
+ // De alguna forma cargar archivos de server
+
client.setScreenAndShow(textEditorScreen);
}
@@ -61,18 +72,31 @@ public class EditorManager {
}
public void save() {
+ EditorFile currentEditorFile = editorFileView.getCurrentEditorFile();
+ if (currentEditorFile == null) return;
+ editorFileView.writeChanges();
+
+ ServerboundSaveCodePayload payload = new ServerboundSaveCodePayload(currentEditorFile);
+ ClientPlayNetworking.send(payload);
}
public void undo() {
}
public void run() {
+ RunInfo runInfo = new RunInfo("main.lua");
+ ServerboundRunCodePayload payload = new ServerboundRunCodePayload(runInfo);
+
+ ClientPlayNetworking.send(payload);
+ /*
String code = files.getFirst().getText();
System.out.println(code);
Lua l = new LuaJ();
l.register("print", Logger.print());
l.run(code);
+
+ */
}
public void redo() {
diff --git a/src/client/java/com/aranroig/client/editor/LuaHighlighter.java b/src/client/java/com/aranroig/client/editor/LuaHighlighter.java
new file mode 100644
index 0000000..cc20c72
--- /dev/null
+++ b/src/client/java/com/aranroig/client/editor/LuaHighlighter.java
@@ -0,0 +1,267 @@
+package com.aranroig.client.editor;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Tokenizes a single raw Lua source line into a list of {@link Span}s, each
+ * carrying the text to render and its ARGB colour. The caller then draws each
+ * span in sequence, advancing the x-position by {@code font.width(span.text())}
+ * between them.
+ *
+ * Handles (within a single line):
+ *
+ * - Single-line comments {@code -- …}
+ * - Keywords
+ * - Built-in global names ({@code print}, {@code pairs}, …)
+ * - Standard library prefixes ({@code math}, {@code string}, …)
+ * - String literals {@code "…"} and {@code '…'}
+ * - Number literals (integer, hex, float)
+ * - Boolean / nil literals {@code true}, {@code false}, {@code nil}
+ * - Operators and punctuation
+ * - Plain identifiers and everything else (default colour)
+ *
+ *
+ * Multi-line strings/comments ({@code [[ … ]]}) require cross-line state;
+ * that state is carried through {@link #tokenizeLine(String, boolean)} via the
+ * {@code inMultilineString} flag, and the method returns whether the caller
+ * should pass {@code true} for the next line.
+ */
+public class LuaHighlighter {
+
+ // ── Token colours (VSCode-dark palette) ──────────────────────────────
+ public static final int C_DEFAULT = 0xFFD4D4D4; // plain identifiers / text
+ public static final int C_KEYWORD = 0xFF569CD6; // language keywords
+ public static final int C_BUILTIN = 0xFF4EC9B0; // built-in functions & globals
+ public static final int C_LIBRARY = 0xFF4EC9B0; // std-lib table names (math, …)
+ public static final int C_STRING = 0xFFCE9178; // string literals
+ public static final int C_NUMBER = 0xFFB5CEA8; // numeric literals
+ public static final int C_COMMENT = 0xFF6A9955; // comments
+ public static final int C_OPERATOR = 0xFFD4D4D4; // operators / punctuation
+ public static final int C_BOOLEAN = 0xFF569CD6; // true / false / nil
+ public static final int C_PARAM = 0xFF9CDCFE; // function parameters (heuristic)
+
+ // ── Lua vocabulary ────────────────────────────────────────────────────
+
+ private static final Set KEYWORDS = Set.of(
+ "and", "break", "do", "else", "elseif", "end",
+ "false", "for", "function", "goto", "if", "in",
+ "local", "nil", "not", "or", "repeat", "return",
+ "then", "true", "until", "while"
+ );
+
+ private static final Set BOOLEAN_NIL = Set.of("true", "false", "nil");
+
+ private static final Set BUILTINS = Set.of(
+ "assert", "collectgarbage", "dofile", "error",
+ "getmetatable", "ipairs", "load", "loadfile", "next",
+ "pairs", "pcall", "print", "rawequal", "rawget",
+ "rawlen", "rawset", "require", "select", "setmetatable",
+ "tonumber", "tostring", "type", "unpack", "xpcall",
+ "_G", "_VERSION"
+ );
+
+ private static final Set LIBRARIES = Set.of(
+ "string", "table", "math", "io", "os",
+ "coroutine", "package", "utf8", "debug"
+ );
+
+ // ── Public types ──────────────────────────────────────────────────────
+
+ /** A single coloured text fragment for one draw call. */
+ public record Span(String text, int color) {}
+
+ /**
+ * Result of tokenizing one line. Carries the spans to draw plus the
+ * multiline-string continuation state for the next line.
+ */
+ public record LineTokens(List spans, boolean continuesMultilineString) {}
+
+ // ── Main API ──────────────────────────────────────────────────────────
+
+ /**
+ * Tokenizes {@code rawLine} (tabs already kept as-is; the caller sanitizes
+ * tabs to spaces for width measurement but we work on the raw chars here).
+ *
+ * @param rawLine the raw source line
+ * @param inMultilineString {@code true} if a {@code [[ }block started on a
+ * previous line and hasn't been closed yet
+ * @return spans + continuation flag
+ */
+ public LineTokens tokenizeLine(String rawLine, boolean inMultilineString) {
+ List spans = new ArrayList<>();
+ int len = rawLine.length();
+ int i = 0;
+
+ // ── Carry-over multi-line string ──────────────────────────────────
+ if (inMultilineString) {
+ int close = rawLine.indexOf("]]");
+ if (close == -1) {
+ // Entire line is still inside the block string
+ spans.add(new Span(rawLine, C_STRING));
+ return new LineTokens(spans, true);
+ } else {
+ // Closing delimiter is on this line
+ spans.add(new Span(rawLine.substring(0, close + 2), C_STRING));
+ i = close + 2;
+ inMultilineString = false;
+ }
+ }
+
+ StringBuilder plain = new StringBuilder();
+
+ while (i < len) {
+ char c = rawLine.charAt(i);
+
+ // ── Single-line comment ───────────────────────────────────────
+ if (c == '-' && i + 1 < len && rawLine.charAt(i + 1) == '-') {
+ flushPlain(spans, plain);
+ // Check for long comment --[[ … ]]
+ if (i + 3 < len && rawLine.charAt(i + 2) == '[' && rawLine.charAt(i + 3) == '[') {
+ int close = rawLine.indexOf("]]", i + 4);
+ if (close == -1) {
+ spans.add(new Span(rawLine.substring(i), C_COMMENT));
+ return new LineTokens(spans, true); // multi-line comment (treated like string)
+ } else {
+ spans.add(new Span(rawLine.substring(i, close + 2), C_COMMENT));
+ i = close + 2;
+ continue;
+ }
+ }
+ // Regular -- comment: rest of line
+ spans.add(new Span(rawLine.substring(i), C_COMMENT));
+ return new LineTokens(spans, false);
+ }
+
+ // ── Multi-line string literal [[ ─────────────────────────────
+ if (c == '[' && i + 1 < len && rawLine.charAt(i + 1) == '[') {
+ flushPlain(spans, plain);
+ int close = rawLine.indexOf("]]", i + 2);
+ if (close == -1) {
+ spans.add(new Span(rawLine.substring(i), C_STRING));
+ return new LineTokens(spans, true);
+ } else {
+ spans.add(new Span(rawLine.substring(i, close + 2), C_STRING));
+ i = close + 2;
+ continue;
+ }
+ }
+
+ // ── String literal " or ' ─────────────────────────────────────
+ if (c == '"' || c == '\'') {
+ flushPlain(spans, plain);
+ char quote = c;
+ int start = i;
+ i++;
+ while (i < len) {
+ char ch = rawLine.charAt(i);
+ if (ch == '\\') { i += 2; continue; } // escape sequence
+ if (ch == quote) { i++; break; }
+ i++;
+ }
+ spans.add(new Span(rawLine.substring(start, i), C_STRING));
+ continue;
+ }
+
+ // ── Number literal ────────────────────────────────────────────
+ if (Character.isDigit(c) || (c == '.' && i + 1 < len && Character.isDigit(rawLine.charAt(i + 1)))) {
+ flushPlain(spans, plain);
+ int start = i;
+ // Hex?
+ if (c == '0' && i + 1 < len && (rawLine.charAt(i + 1) == 'x' || rawLine.charAt(i + 1) == 'X')) {
+ i += 2;
+ while (i < len && isHexDigit(rawLine.charAt(i))) i++;
+ } else {
+ while (i < len && (Character.isDigit(rawLine.charAt(i)) || rawLine.charAt(i) == '.')) i++;
+ if (i < len && (rawLine.charAt(i) == 'e' || rawLine.charAt(i) == 'E')) {
+ i++;
+ if (i < len && (rawLine.charAt(i) == '+' || rawLine.charAt(i) == '-')) i++;
+ while (i < len && Character.isDigit(rawLine.charAt(i))) i++;
+ }
+ }
+ spans.add(new Span(rawLine.substring(start, i), C_NUMBER));
+ continue;
+ }
+
+ // ── Identifier or keyword ─────────────────────────────────────
+ if (Character.isLetter(c) || c == '_') {
+ flushPlain(spans, plain);
+ int start = i;
+ while (i < len && (Character.isLetterOrDigit(rawLine.charAt(i)) || rawLine.charAt(i) == '_')) i++;
+ String word = rawLine.substring(start, i);
+
+ // Peek ahead: if followed by '(' it's a call — highlight as builtin/method
+ boolean isCall = (i < len && rawLine.charAt(i) == '(');
+ // Peek behind: if preceded by ':' or '.' it's a method — leave as default
+ boolean isMember = (start > 0 && (rawLine.charAt(start - 1) == '.' || rawLine.charAt(start - 1) == ':'));
+
+ if (BOOLEAN_NIL.contains(word)) {
+ spans.add(new Span(word, C_BOOLEAN));
+ } else if (KEYWORDS.contains(word)) {
+ spans.add(new Span(word, C_KEYWORD));
+ } else if (!isMember && LIBRARIES.contains(word)) {
+ spans.add(new Span(word, C_LIBRARY));
+ } else if (!isMember && BUILTINS.contains(word)) {
+ spans.add(new Span(word, C_BUILTIN));
+ } else if (isCall) {
+ // User-defined function call: slightly highlighted
+ spans.add(new Span(word, C_BUILTIN));
+ } else {
+ spans.add(new Span(word, C_DEFAULT));
+ }
+ continue;
+ }
+
+ // ── Operator / punctuation ────────────────────────────────────
+ if (isOperator(c)) {
+ flushPlain(spans, plain);
+ // Grab multi-char operators: ~=, ==, <=, >=, .., ...
+ if (i + 2 < len && rawLine.charAt(i) == '.' && rawLine.charAt(i+1) == '.' && rawLine.charAt(i+2) == '.') {
+ spans.add(new Span("...", C_OPERATOR)); i += 3;
+ } else if (i + 1 < len && isTwoCharOp(rawLine, i)) {
+ spans.add(new Span(rawLine.substring(i, i + 2), C_OPERATOR)); i += 2;
+ } else {
+ spans.add(new Span(String.valueOf(c), C_OPERATOR)); i++;
+ }
+ continue;
+ }
+
+ // ── Everything else (spaces, tabs, unknown) ───────────────────
+ plain.append(c);
+ i++;
+ }
+
+ flushPlain(spans, plain);
+ return new LineTokens(spans, false);
+ }
+
+ // ── Helpers ───────────────────────────────────────────────────────────
+
+ private static void flushPlain(List spans, StringBuilder sb) {
+ if (sb.length() > 0) {
+ spans.add(new Span(sb.toString(), C_DEFAULT));
+ sb.setLength(0);
+ }
+ }
+
+ private static boolean isHexDigit(char c) {
+ return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F');
+ }
+
+ private static boolean isOperator(char c) {
+ return "+-*/%^#&|~<>=(){}[];:,./\\".indexOf(c) >= 0;
+ }
+
+ private static boolean isTwoCharOp(String line, int i) {
+ char a = line.charAt(i), b = line.charAt(i + 1);
+ return (a == '=' && b == '=')
+ || (a == '~' && b == '=')
+ || (a == '<' && b == '=')
+ || (a == '>' && b == '=')
+ || (a == '.' && b == '.')
+ || (a == ':' && b == ':')
+ || (a == '<' && b == '<')
+ || (a == '>' && b == '>');
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/aranroig/Codecraft.java b/src/main/java/com/aranroig/Codecraft.java
index c7f0ecb..43a6291 100644
--- a/src/main/java/com/aranroig/Codecraft.java
+++ b/src/main/java/com/aranroig/Codecraft.java
@@ -1,5 +1,7 @@
package com.aranroig;
+import com.aranroig.editor.EditorFile;
+import com.aranroig.payloads.ServerboundRunCodePayload;
import com.aranroig.payloads.ServerboundSaveCodePayload;
import net.fabricmc.api.ModInitializer;
@@ -7,9 +9,13 @@ import net.fabricmc.fabric.api.networking.v1.PayloadTypeRegistry;
import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking;
import net.minecraft.resources.Identifier;
+import net.minecraft.server.MinecraftServer;
+import net.minecraft.world.entity.vehicle.minecart.Minecart;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import java.util.List;
+
public class Codecraft implements ModInitializer {
public static final String MOD_ID = "codecraft";
@@ -18,6 +24,8 @@ public class Codecraft implements ModInitializer {
// That way, it's clear which mod wrote info, warnings, and errors.
public static final Logger LOGGER = LoggerFactory.getLogger(MOD_ID);
+ private List editorFiles;
+
@Override
public void onInitialize() {
// This code runs as soon as Minecraft is in a mod-load-ready state.
@@ -26,10 +34,49 @@ public class Codecraft implements ModInitializer {
LOGGER.info("Hello Fabric world!");
PayloadTypeRegistry.serverboundPlay().register(ServerboundSaveCodePayload.TYPE, ServerboundSaveCodePayload.CODEC);
+ PayloadTypeRegistry.serverboundPlay().register(ServerboundRunCodePayload.TYPE, ServerboundRunCodePayload.CODEC);
ServerPlayNetworking.registerGlobalReceiver(ServerboundSaveCodePayload.TYPE, (payload, context) -> {
- System.out.println(payload.data().getName());
- System.out.println(payload.data().getText());
+ MinecraftServer server = context.player().level().getServer();
+ if(server == null) return;
+
+ SavedProjectData savedData = SavedProjectData.getSavedBlockData(server);
+ List files = savedData.getEditorFiles();
+
+ EditorFile editorFile = payload.data();
+ boolean existing = false;
+ for(EditorFile file : files) {
+ if(file.getName().equals(editorFile.getName())) {
+ existing = true;
+ file.setText(editorFile.getText());
+ break;
+ }
+ }
+ if(!existing) {
+ files.add(editorFile);
+ }
+
+ savedData.setEditorFiles(files);
+ System.out.println("Successfully saved files");
+ });
+
+ ServerPlayNetworking.registerGlobalReceiver(ServerboundRunCodePayload.TYPE, (payload, context) -> {
+ // HERE
+ MinecraftServer server = context.player().level().getServer();
+ if(server == null) return;
+
+ SavedProjectData savedData = SavedProjectData.getSavedBlockData(server);
+ List files = savedData.getEditorFiles();
+
+ String entrypoint = payload.data().getEntrypoint();
+ System.out.println("Want to run the code " + payload.data().getEntrypoint());
+
+ // TODO: Run the entrypoint, might change later
+ for(EditorFile file : files) {
+ if(file.getName().equals(entrypoint)) {
+ System.out.println(file.getText());
+ }
+ }
});
}
diff --git a/src/main/java/com/aranroig/SavedProjectData.java b/src/main/java/com/aranroig/SavedProjectData.java
new file mode 100644
index 0000000..f63e382
--- /dev/null
+++ b/src/main/java/com/aranroig/SavedProjectData.java
@@ -0,0 +1,65 @@
+package com.aranroig;
+
+import com.aranroig.editor.EditorFile;
+import com.mojang.serialization.Codec;
+import com.mojang.serialization.codecs.RecordCodecBuilder;
+import net.minecraft.resources.Identifier;
+import net.minecraft.server.MinecraftServer;
+import net.minecraft.server.level.ServerLevel;
+import net.minecraft.world.level.saveddata.SavedData;
+import net.minecraft.world.level.saveddata.SavedDataType;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class SavedProjectData extends SavedData {
+ private List editorFiles;
+
+ public SavedProjectData() {
+ editorFiles = new ArrayList();
+ }
+
+ public SavedProjectData(List editorFiles) {
+ this.editorFiles = editorFiles;
+ }
+
+ public List getEditorFiles() {
+ return editorFiles;
+ }
+
+ public void setEditorFiles(List editorFiles) {
+ this.editorFiles = editorFiles;
+ setDirty();
+ }
+
+
+ private static final Codec CODEC = RecordCodecBuilder.create(instance ->
+ instance.group(
+ EditorFile.CODEC.listOf()
+ .fieldOf("editor_files")
+ .forGetter(SavedProjectData::getEditorFiles)
+ ).apply(instance, SavedProjectData::new)
+ );
+
+ private static final SavedDataType TYPE = new SavedDataType(
+ Identifier.fromNamespaceAndPath(Codecraft.MOD_ID, "saved_project_data"), // The unique name for this saved data.
+ SavedProjectData::new, // If there's no 'SavedBlockData', yet create one and refresh fields.
+ CODEC, // The codec used for serialization/deserialization.
+ null // A data fixer, which is not needed here.
+ );
+
+ public static SavedProjectData getSavedBlockData(MinecraftServer server) {
+ // This could be either the overworld or another dimension.
+ ServerLevel level = server.getLevel(ServerLevel.OVERWORLD);
+
+ if (level == null) {
+ return new SavedProjectData(); // Return a new instance if the level is null.
+ }
+
+ // The first time the following 'computeIfAbsent' function is called, it creates a new 'SavedBlockData'
+ // instance and stores it inside the 'DimensionDataStorage'.
+ // Subsequent calls to 'computeIfAbsent' returns the saved 'SavedBlockData' NBT on disk to the Codec in our type,
+ // using the Codec to decode the NBT into our saved data.
+ return level.getDataStorage().computeIfAbsent(TYPE);
+ }
+}
diff --git a/src/main/java/com/aranroig/editor/EditorFile.java b/src/main/java/com/aranroig/editor/EditorFile.java
index 393781f..0eb5371 100644
--- a/src/main/java/com/aranroig/editor/EditorFile.java
+++ b/src/main/java/com/aranroig/editor/EditorFile.java
@@ -1,11 +1,13 @@
package com.aranroig.editor;
+import com.mojang.serialization.Codec;
+import com.mojang.serialization.codecs.RecordCodecBuilder;
import net.minecraft.network.RegistryFriendlyByteBuf;
import net.minecraft.network.codec.ByteBufCodecs;
import net.minecraft.network.codec.StreamCodec;
public class EditorFile {
- public static final StreamCodec CODEC =
+ public static final StreamCodec STREAM_CODEC =
StreamCodec.composite(
ByteBufCodecs.STRING_UTF8,
EditorFile::getName,
@@ -16,6 +18,13 @@ public class EditorFile {
EditorFile::new
);
+ public static final Codec CODEC = RecordCodecBuilder.create(instance ->
+ instance.group(
+ Codec.STRING.fieldOf("name").forGetter(EditorFile::getName),
+ Codec.STRING.fieldOf("text").forGetter(EditorFile::getText)
+ ).apply(instance, EditorFile::new)
+ );
+
// Class with file info
private String name;
private String text;
diff --git a/src/main/java/com/aranroig/editor/Project.java b/src/main/java/com/aranroig/editor/Project.java
new file mode 100644
index 0000000..3b28f49
--- /dev/null
+++ b/src/main/java/com/aranroig/editor/Project.java
@@ -0,0 +1,9 @@
+package com.aranroig.editor;
+
+import java.util.List;
+
+public record Project(List editorFiles) {
+ /*
+ TODO: Acabar esto. En principio hay que sustituir las listas de los archivos de proyecto por esta clase
+ */
+}
diff --git a/src/main/java/com/aranroig/editor/RunInfo.java b/src/main/java/com/aranroig/editor/RunInfo.java
new file mode 100644
index 0000000..d8da231
--- /dev/null
+++ b/src/main/java/com/aranroig/editor/RunInfo.java
@@ -0,0 +1,30 @@
+package com.aranroig.editor;
+
+import net.minecraft.network.RegistryFriendlyByteBuf;
+import net.minecraft.network.codec.ByteBufCodecs;
+import net.minecraft.network.codec.StreamCodec;
+
+public class RunInfo {
+ public static final StreamCodec CODEC =
+ StreamCodec.composite(
+ ByteBufCodecs.STRING_UTF8,
+ RunInfo::getEntrypoint,
+
+ RunInfo::new
+ );
+
+ // Class with file info
+ private String entrypoint;
+
+ public RunInfo(String entrypoint) {
+ this.entrypoint = entrypoint;
+ }
+
+ public String getEntrypoint() {
+ return entrypoint;
+ }
+
+ public void setEntrypoint(String entrypoint) {
+ this.entrypoint = entrypoint;
+ }
+}
diff --git a/src/main/java/com/aranroig/payloads/ClientboundSaveCodePayload.java b/src/main/java/com/aranroig/payloads/ClientboundSaveCodePayload.java
index 197f800..5c94c1e 100644
--- a/src/main/java/com/aranroig/payloads/ClientboundSaveCodePayload.java
+++ b/src/main/java/com/aranroig/payloads/ClientboundSaveCodePayload.java
@@ -14,7 +14,7 @@ public record ClientboundSaveCodePayload(EditorFile data) implements CustomPacke
public static final CustomPacketPayload.Type TYPE = new CustomPacketPayload.Type<>(SAVE_CODE_PAYLOAD_ID);
public static final StreamCodec CODEC =
- StreamCodec.composite(EditorFile.CODEC, ClientboundSaveCodePayload::data, ClientboundSaveCodePayload::new);
+ StreamCodec.composite(EditorFile.STREAM_CODEC, ClientboundSaveCodePayload::data, ClientboundSaveCodePayload::new);
@Override
public Type extends CustomPacketPayload> type() {
diff --git a/src/main/java/com/aranroig/payloads/ServerboundRunCodePayload.java b/src/main/java/com/aranroig/payloads/ServerboundRunCodePayload.java
new file mode 100644
index 0000000..43aacb2
--- /dev/null
+++ b/src/main/java/com/aranroig/payloads/ServerboundRunCodePayload.java
@@ -0,0 +1,24 @@
+package com.aranroig.payloads;
+
+import com.aranroig.Codecraft;
+import com.aranroig.editor.EditorFile;
+import com.aranroig.editor.RunInfo;
+import net.minecraft.network.RegistryFriendlyByteBuf;
+import net.minecraft.network.codec.StreamCodec;
+import net.minecraft.network.protocol.common.custom.CustomPacketPayload;
+import net.minecraft.resources.Identifier;
+
+public record ServerboundRunCodePayload(RunInfo data) implements CustomPacketPayload {
+ public static final Identifier RUN_CODE_PAYLOAD_ID = Identifier.fromNamespaceAndPath(Codecraft.MOD_ID, "run_code");
+
+ public static final CustomPacketPayload.Type TYPE = new CustomPacketPayload.Type<>(RUN_CODE_PAYLOAD_ID);
+
+ public static final StreamCodec CODEC =
+ StreamCodec.composite(RunInfo.CODEC, ServerboundRunCodePayload::data, ServerboundRunCodePayload::new);
+
+
+ @Override
+ public Type extends CustomPacketPayload> type() {
+ return TYPE;
+ }
+}
diff --git a/src/main/java/com/aranroig/payloads/ServerboundSaveCodePayload.java b/src/main/java/com/aranroig/payloads/ServerboundSaveCodePayload.java
index 2deb09c..93dc2d6 100644
--- a/src/main/java/com/aranroig/payloads/ServerboundSaveCodePayload.java
+++ b/src/main/java/com/aranroig/payloads/ServerboundSaveCodePayload.java
@@ -13,7 +13,7 @@ public record ServerboundSaveCodePayload(EditorFile data) implements CustomPacke
public static final CustomPacketPayload.Type TYPE = new CustomPacketPayload.Type<>(SAVE_CODE_PAYLOAD_ID);
public static final StreamCodec CODEC =
- StreamCodec.composite(EditorFile.CODEC, ServerboundSaveCodePayload::data, ServerboundSaveCodePayload::new);
+ StreamCodec.composite(EditorFile.STREAM_CODEC, ServerboundSaveCodePayload::data, ServerboundSaveCodePayload::new);
@Override
public CustomPacketPayload.Type extends CustomPacketPayload> type() {