···11+# grimoire
22+33+[DFU](https://github.com/Mojang/DataFixerUpper) Codecs for various libraries. Builds are published
44+to [repo.nayrid.com](https://repo.nayrid.com/#).
···11+@rem
22+@rem Copyright 2015 the original author or authors.
33+@rem
44+@rem Licensed under the Apache License, Version 2.0 (the "License");
55+@rem you may not use this file except in compliance with the License.
66+@rem You may obtain a copy of the License at
77+@rem
88+@rem https://www.apache.org/licenses/LICENSE-2.0
99+@rem
1010+@rem Unless required by applicable law or agreed to in writing, software
1111+@rem distributed under the License is distributed on an "AS IS" BASIS,
1212+@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1313+@rem See the License for the specific language governing permissions and
1414+@rem limitations under the License.
1515+@rem
1616+@rem SPDX-License-Identifier: Apache-2.0
1717+@rem
1818+1919+@if "%DEBUG%"=="" @echo off
2020+@rem ##########################################################################
2121+@rem
2222+@rem Gradle startup script for Windows
2323+@rem
2424+@rem ##########################################################################
2525+2626+@rem Set local scope for the variables with windows NT shell
2727+if "%OS%"=="Windows_NT" setlocal
2828+2929+set DIRNAME=%~dp0
3030+if "%DIRNAME%"=="" set DIRNAME=.
3131+@rem This is normally unused
3232+set APP_BASE_NAME=%~n0
3333+set APP_HOME=%DIRNAME%
3434+3535+@rem Resolve any "." and ".." in APP_HOME to make it shorter.
3636+for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
3737+3838+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
3939+set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
4040+4141+@rem Find java.exe
4242+if defined JAVA_HOME goto findJavaFromJavaHome
4343+4444+set JAVA_EXE=java.exe
4545+%JAVA_EXE% -version >NUL 2>&1
4646+if %ERRORLEVEL% equ 0 goto execute
4747+4848+echo. 1>&2
4949+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
5050+echo. 1>&2
5151+echo Please set the JAVA_HOME variable in your environment to match the 1>&2
5252+echo location of your Java installation. 1>&2
5353+5454+goto fail
5555+5656+:findJavaFromJavaHome
5757+set JAVA_HOME=%JAVA_HOME:"=%
5858+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
5959+6060+if exist "%JAVA_EXE%" goto execute
6161+6262+echo. 1>&2
6363+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
6464+echo. 1>&2
6565+echo Please set the JAVA_HOME variable in your environment to match the 1>&2
6666+echo location of your Java installation. 1>&2
6767+6868+goto fail
6969+7070+:execute
7171+@rem Setup the command line
7272+7373+7474+7575+@rem Execute Gradle
7676+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %*
7777+7878+:end
7979+@rem End local scope for the variables with windows NT shell
8080+if %ERRORLEVEL% equ 0 goto mainEnd
8181+8282+:fail
8383+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
8484+rem the _cmd.exe /c_ return code!
8585+set EXIT_CODE=%ERRORLEVEL%
8686+if %EXIT_CODE% equ 0 set EXIT_CODE=1
8787+if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
8888+exit /b %EXIT_CODE%
8989+9090+:mainEnd
9191+if "%OS%"=="Windows_NT" endlocal
9292+9393+:omega
···11+/*
22+ * This file is part of grimoire, licensed under the MIT License.
33+ *
44+ * Copyright (c) 2026 nayrid
55+ *
66+ * Permission is hereby granted, free of charge, to any person obtaining a copy
77+ * of this software and associated documentation files (the "Software"), to deal
88+ * in the Software without restriction, including without limitation the rights
99+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
1010+ * copies of the Software, and to permit persons to whom the Software is
1111+ * furnished to do so, subject to the following conditions:
1212+ *
1313+ * The above copyright notice and this permission notice shall be included in all
1414+ * copies or substantial portions of the Software.
1515+ *
1616+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1717+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1818+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
1919+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
2020+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
2121+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
2222+ * SOFTWARE.
2323+ */
2424+package com.nayrid.grimoire.adventure;
2525+2626+import com.mojang.serialization.Codec;
2727+import com.nayrid.grimoire.common.Grimoire;
2828+import net.kyori.adventure.key.Key;
2929+3030+/**
3131+ * Codecs for adventure-key types.
3232+ *
3333+ * @since 1.0.0
3434+ */
3535+public final class KeyCodecs {
3636+3737+ /**
3838+ * Stores a {@link Key} as its minimal string representation.
3939+ *
4040+ * @since 1.0.0
4141+ */
4242+ @SuppressWarnings("PatternValidation")
4343+ public static final Codec<Key> KEY =
4444+ Codec.STRING.comapFlatMap(string -> Grimoire.safeDataResult(() -> Key.key(
4545+ string)), Key::asMinimalString);
4646+4747+ private KeyCodecs() {
4848+ throw new IllegalStateException("Utility class");
4949+ }
5050+5151+}
···11+/*
22+ * This file is part of grimoire, licensed under the MIT License.
33+ *
44+ * Copyright (c) 2026 nayrid
55+ *
66+ * Permission is hereby granted, free of charge, to any person obtaining a copy
77+ * of this software and associated documentation files (the "Software"), to deal
88+ * in the Software without restriction, including without limitation the rights
99+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
1010+ * copies of the Software, and to permit persons to whom the Software is
1111+ * furnished to do so, subject to the following conditions:
1212+ *
1313+ * The above copyright notice and this permission notice shall be included in all
1414+ * copies or substantial portions of the Software.
1515+ *
1616+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1717+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1818+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
1919+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
2020+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
2121+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
2222+ * SOFTWARE.
2323+ */
2424+package com.nayrid.grimoire.common;
2525+2626+import com.mojang.serialization.DataResult;
2727+import org.jspecify.annotations.Nullable;
2828+2929+import java.util.Arrays;
3030+import java.util.List;
3131+import java.util.function.Supplier;
3232+import java.util.stream.IntStream;
3333+3434+/**
3535+ * Utilities for writing codecs.
3636+ *
3737+ * @since 1.0.0
3838+ */
3939+public final class Grimoire {
4040+4141+ private Grimoire() {
4242+ throw new IllegalStateException("Utility class");
4343+ }
4444+4545+ /**
4646+ * Ensures a list's length is equal to `length`.
4747+ *
4848+ * @param list the list
4949+ * @param length the length
5050+ * @param <T> list type
5151+ * @return data result
5252+ * @since 1.0.0
5353+ */
5454+ public static <T> DataResult<List<T>> fixedLength(final List<T> list, final int length) {
5555+ if (list.size() != length) {
5656+ final Supplier<String> messageSupplier =
5757+ () -> "Input is not a list of %d elements".formatted(length);
5858+ return list.size() >= length ? DataResult.error(messageSupplier,
5959+ list.subList(0, length)
6060+ ) : DataResult.error(messageSupplier);
6161+ } else {
6262+ return DataResult.success(list);
6363+ }
6464+ }
6565+6666+ /**
6767+ * Ensures an {@link IntStream}'s length is equal to `length`.
6868+ *
6969+ * @param stream the stream
7070+ * @param length the length
7171+ * @return data result
7272+ * @since 1.0.0
7373+ */
7474+ public static DataResult<int[]> fixedLength(final IntStream stream, final int length) {
7575+ final int[] ints = stream.limit(length + 1).toArray();
7676+ if (ints.length != length) {
7777+ final Supplier<String> messageSupplier =
7878+ () -> "Input is not a list of %d integers".formatted(length);
7979+ return ints.length >= length ? DataResult.error(messageSupplier, Arrays.copyOf(ints,
8080+ length)) : DataResult.error(messageSupplier);
8181+ } else {
8282+ return DataResult.success(ints);
8383+ }
8484+ }
8585+8686+ /**
8787+ * Resolves a {@link Supplier} and wraps the result inside a {@link DataResult}.
8888+ *
8989+ * @param supplier the supplier
9090+ * @param <T> result type
9191+ * @return data result
9292+ * @since 1.0.0
9393+ */
9494+ public static <T> DataResult<T> safeDataResult(final Supplier<T> supplier) {
9595+ try {
9696+ final T t = supplier.get();
9797+ return DataResult.success(t);
9898+ } catch (final Exception exception) {
9999+ return DataResult.error(exception::getMessage);
100100+ }
101101+ }
102102+103103+ /**
104104+ * Returns a {@link DataResult} for a nullable value ensuring it's non-null.
105105+ *
106106+ * @param t the nullable thing
107107+ * @param <T> result type
108108+ * @return data result
109109+ * @since 1.0.0
110110+ */
111111+ public static <T> DataResult<T> nonNullResult(final @Nullable T t) {
112112+ if (t == null) {
113113+ return DataResult.error(() -> "Cannot be null");
114114+ }
115115+ //noinspection NullableProblems - no better way of doing this :(
116116+ return DataResult.success(t);
117117+ }
118118+119119+}
···11+/*
22+ * This file is part of grimoire, licensed under the MIT License.
33+ *
44+ * Copyright (c) 2026 nayrid
55+ *
66+ * Permission is hereby granted, free of charge, to any person obtaining a copy
77+ * of this software and associated documentation files (the "Software"), to deal
88+ * in the Software without restriction, including without limitation the rights
99+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
1010+ * copies of the Software, and to permit persons to whom the Software is
1111+ * furnished to do so, subject to the following conditions:
1212+ *
1313+ * The above copyright notice and this permission notice shall be included in all
1414+ * copies or substantial portions of the Software.
1515+ *
1616+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1717+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1818+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
1919+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
2020+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
2121+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
2222+ * SOFTWARE.
2323+ */
2424+package com.nayrid.grimoire.common;
2525+2626+import com.mojang.serialization.Codec;
2727+2828+import java.util.Arrays;
2929+import java.util.UUID;
3030+3131+/**
3232+ * Codecs for Java standard library types.
3333+ *
3434+ * @since 1.0.0
3535+ */
3636+public final class JavaCodecs {
3737+3838+ /**
3939+ * Stores a {@link UUID} as a string.
4040+ *
4141+ * @since 1.0.0
4242+ */
4343+ public static final Codec<UUID> UUID_STRING =
4444+ Codec.STRING.comapFlatMap(string -> Grimoire.safeDataResult(() -> java.util.UUID.fromString(
4545+ string)), java.util.UUID::toString);
4646+4747+ /**
4848+ * Stores a {@link UUID} as a list of 4 integers.
4949+ *
5050+ * @since 1.0.0
5151+ */
5252+ public static final Codec<UUID> UUID_INTS = Codec.INT_STREAM
5353+ .comapFlatMap(list -> Grimoire.fixedLength(list, 4).map(JavaCodecs::uuidFromIntArray),
5454+ uuid -> Arrays.stream(uuidToIntArray(uuid)));
5555+5656+ private JavaCodecs() {
5757+ throw new IllegalStateException("Utility class");
5858+ }
5959+6060+ private static UUID uuidFromIntArray(final int[] intArray) {
6161+ return new UUID((long) intArray[0] << 32 | intArray[1] & 4294967295L,
6262+ (long) intArray[2] << 32 | intArray[3] & 4294967295L);
6363+ }
6464+6565+ private static int[] uuidToIntArray(final UUID uuid) {
6666+ return mostLeastAsIntArray(uuid.getMostSignificantBits(), uuid.getLeastSignificantBits());
6767+ }
6868+6969+ private static int[] mostLeastAsIntArray(final long mostSignificantBits,
7070+ final long leastSignificantBits) {
7171+ return new int[]{(int) (mostSignificantBits >> 32), (int) mostSignificantBits,
7272+ (int) (leastSignificantBits >> 32), (int) leastSignificantBits};
7373+ }
7474+7575+}
···11+/**
22+ * Codecs for paper types.
33+ */
44+@NullMarked
55+package com.nayrid.grimoire.paper;
66+77+import org.jspecify.annotations.NullMarked;
+21
license.txt
···11+MIT License
22+33+Copyright (c) 2026 nayrid
44+55+Permission is hereby granted, free of charge, to any person obtaining a copy
66+of this software and associated documentation files (the "Software"), to deal
77+in the Software without restriction, including without limitation the rights
88+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
99+copies of the Software, and to permit persons to whom the Software is
1010+furnished to do so, subject to the following conditions:
1111+1212+The above copyright notice and this permission notice shall be included in all
1313+copies or substantial portions of the Software.
1414+1515+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1616+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1717+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
1818+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
1919+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
2020+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
2121+SOFTWARE.
+21
license_header.txt
···11+This file is part of grimoire, licensed under the MIT License.
22+33+Copyright (c) 2026 nayrid
44+55+Permission is hereby granted, free of charge, to any person obtaining a copy
66+of this software and associated documentation files (the "Software"), to deal
77+in the Software without restriction, including without limitation the rights
88+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
99+copies of the Software, and to permit persons to whom the Software is
1010+furnished to do so, subject to the following conditions:
1111+1212+The above copyright notice and this permission notice shall be included in all
1313+copies or substantial portions of the Software.
1414+1515+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1616+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1717+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
1818+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
1919+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
2020+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
2121+SOFTWARE.