mirror of
https://github.com/GeyserMC/Rainbow.git
synced 2025-12-19 14:59:16 +00:00
Working on some stuff, basic mapping generator
This commit is contained in:
@@ -1,14 +1,14 @@
|
|||||||
org.gradle.jvmargs=-Xmx1G
|
org.gradle.jvmargs=-Xmx1G
|
||||||
|
|
||||||
# Fabric Properties
|
# Fabric Properties
|
||||||
minecraft_version=1.21.4
|
minecraft_version=1.21.6
|
||||||
parchment_version=1.21.4:2025.02.16
|
parchment_version=1.21.5:2025.06.15
|
||||||
loader_version=0.16.10
|
loader_version=0.16.14
|
||||||
|
|
||||||
# Mod Properties
|
# Mod Properties
|
||||||
mod_version=0.0.1-1.21.4
|
mod_version=0.0.1-1.21.6
|
||||||
maven_group=xyz.eclipseisoffline
|
maven_group=xyz.eclipseisoffline
|
||||||
archives_base_name=geyser-mappings-generator
|
archives_base_name=geyser-mappings-generator
|
||||||
|
|
||||||
# Dependencies
|
# Dependencies
|
||||||
fabric_version=0.118.5+1.21.4
|
fabric_version=0.128.1+1.21.6
|
||||||
|
|||||||
32
src/main/java/xyz/eclipseisoffline/geyser/GeyserMapping.java
Normal file
32
src/main/java/xyz/eclipseisoffline/geyser/GeyserMapping.java
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
package xyz.eclipseisoffline.geyser;
|
||||||
|
|
||||||
|
import com.mojang.serialization.Codec;
|
||||||
|
import com.mojang.serialization.codecs.RecordCodecBuilder;
|
||||||
|
import net.minecraft.core.component.DataComponentMap;
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
public record GeyserMapping(ResourceLocation model, ResourceLocation bedrockIdentifier, BedrockOptions bedrockOptions, DataComponentMap components) {
|
||||||
|
public static final Codec<GeyserMapping> CODEC = RecordCodecBuilder.create(instance ->
|
||||||
|
instance.group(
|
||||||
|
Codec.STRING.fieldOf("type").forGetter(mapping -> "definition"),
|
||||||
|
ResourceLocation.CODEC.fieldOf("model").forGetter(GeyserMapping::model),
|
||||||
|
ResourceLocation.CODEC.fieldOf("bedrock_identifier").forGetter(GeyserMapping::bedrockIdentifier),
|
||||||
|
BedrockOptions.CODEC.fieldOf("bedrock_options").forGetter(GeyserMapping::bedrockOptions),
|
||||||
|
DataComponentMap.CODEC.fieldOf("components").forGetter(GeyserMapping::components)
|
||||||
|
).apply(instance, (type, model, bedrockIdentifier, bedrockOptions, components)
|
||||||
|
-> new GeyserMapping(model, bedrockIdentifier, bedrockOptions, components))
|
||||||
|
);
|
||||||
|
|
||||||
|
public record BedrockOptions(Optional<String> icon, boolean allowOffhand, boolean displayHandheld, int protectionValue) {
|
||||||
|
public static final Codec<BedrockOptions> CODEC = RecordCodecBuilder.create(instance ->
|
||||||
|
instance.group(
|
||||||
|
Codec.STRING.optionalFieldOf("icon").forGetter(BedrockOptions::icon),
|
||||||
|
Codec.BOOL.fieldOf("allow_offhand").forGetter(BedrockOptions::allowOffhand),
|
||||||
|
Codec.BOOL.fieldOf("display_handheld").forGetter(BedrockOptions::displayHandheld),
|
||||||
|
Codec.INT.fieldOf("protection_value").forGetter(BedrockOptions::protectionValue)
|
||||||
|
).apply(instance, BedrockOptions::new)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
package xyz.eclipseisoffline.geyser;
|
||||||
|
|
||||||
|
import com.google.common.collect.Multimap;
|
||||||
|
import com.google.common.collect.MultimapBuilder;
|
||||||
|
import com.mojang.serialization.Codec;
|
||||||
|
import com.mojang.serialization.codecs.RecordCodecBuilder;
|
||||||
|
import net.minecraft.core.Holder;
|
||||||
|
import net.minecraft.world.item.Item;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
public class GeyserMappings {
|
||||||
|
private static final Codec<Map<Holder<Item>, Collection<GeyserMapping>>> MAPPINGS_CODEC = Codec.unboundedMap(Item.CODEC, GeyserMapping.CODEC.listOf().xmap(Function.identity(), ArrayList::new));
|
||||||
|
|
||||||
|
public static final Codec<GeyserMappings> CODEC = RecordCodecBuilder.create(instance ->
|
||||||
|
instance.group(
|
||||||
|
Codec.INT.fieldOf("format_version").forGetter(mappings -> 2),
|
||||||
|
MAPPINGS_CODEC.fieldOf("items").forGetter(GeyserMappings::mappings)
|
||||||
|
).apply(instance, (format, mappings) -> new GeyserMappings(mappings))
|
||||||
|
);
|
||||||
|
|
||||||
|
private final Multimap<Holder<Item>, GeyserMapping> mappings = MultimapBuilder.hashKeys().hashSetValues().build();
|
||||||
|
|
||||||
|
public GeyserMappings() {}
|
||||||
|
|
||||||
|
private GeyserMappings(Map<Holder<Item>, Collection<GeyserMapping>> mappings) {
|
||||||
|
for (Holder<Item> item : mappings.keySet()) {
|
||||||
|
this.mappings.putAll(item, mappings.get(item));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void map(Holder<Item> item, GeyserMapping mapping) {
|
||||||
|
// TODO conflict detection
|
||||||
|
mappings.put(item, mapping);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<Holder<Item>, Collection<GeyserMapping>> mappings() {
|
||||||
|
return mappings.asMap();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,11 +1,48 @@
|
|||||||
package xyz.eclipseisoffline.geyser;
|
package xyz.eclipseisoffline.geyser;
|
||||||
|
|
||||||
|
import com.mojang.brigadier.arguments.StringArgumentType;
|
||||||
|
import com.mojang.logging.LogUtils;
|
||||||
import net.fabricmc.api.ClientModInitializer;
|
import net.fabricmc.api.ClientModInitializer;
|
||||||
|
import net.fabricmc.fabric.api.client.command.v2.ClientCommandManager;
|
||||||
|
import net.fabricmc.fabric.api.client.command.v2.ClientCommandRegistrationCallback;
|
||||||
|
import net.minecraft.network.chat.Component;
|
||||||
|
import net.minecraft.world.item.ItemStack;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
|
||||||
public class GeyserMappingsGenerator implements ClientModInitializer {
|
public class GeyserMappingsGenerator implements ClientModInitializer {
|
||||||
|
|
||||||
|
public static final Logger LOGGER = LogUtils.getLogger();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onInitializeClient() {
|
public void onInitializeClient() {
|
||||||
|
ClientCommandRegistrationCallback.EVENT.register((dispatcher, buildContext) -> {
|
||||||
|
dispatcher.register(ClientCommandManager.literal("geyser")
|
||||||
|
.then(ClientCommandManager.literal("create")
|
||||||
|
.then(ClientCommandManager.argument("name", StringArgumentType.word())
|
||||||
|
.executes(context -> {
|
||||||
|
String name = StringArgumentType.getString(context, "name");
|
||||||
|
PackManager.getInstance().startPack(name);
|
||||||
|
context.getSource().sendFeedback(Component.literal("Created pack with name " + name));
|
||||||
|
return 0;
|
||||||
|
})
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.then(ClientCommandManager.literal("map")
|
||||||
|
.executes(context -> {
|
||||||
|
ItemStack heldItem = context.getSource().getPlayer().getMainHandItem();
|
||||||
|
PackManager.getInstance().map(heldItem);
|
||||||
|
context.getSource().sendFeedback(Component.literal("Added held item to Geyser mappings"));
|
||||||
|
return 0;
|
||||||
|
})
|
||||||
|
)
|
||||||
|
.then(ClientCommandManager.literal("finish")
|
||||||
|
.executes(context -> {
|
||||||
|
PackManager.getInstance().finish();
|
||||||
|
context.getSource().sendFeedback(Component.literal("Wrote pack to disk"));
|
||||||
|
return 0;
|
||||||
|
})
|
||||||
|
)
|
||||||
|
);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
98
src/main/java/xyz/eclipseisoffline/geyser/PackManager.java
Normal file
98
src/main/java/xyz/eclipseisoffline/geyser/PackManager.java
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
package xyz.eclipseisoffline.geyser;
|
||||||
|
|
||||||
|
import com.google.gson.FormattingStyle;
|
||||||
|
import com.google.gson.Gson;
|
||||||
|
import com.google.gson.GsonBuilder;
|
||||||
|
import com.google.gson.JsonElement;
|
||||||
|
import com.google.gson.stream.JsonWriter;
|
||||||
|
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||||
|
import com.mojang.brigadier.exceptions.SimpleCommandExceptionType;
|
||||||
|
import com.mojang.serialization.JsonOps;
|
||||||
|
import net.fabricmc.loader.api.FabricLoader;
|
||||||
|
import net.minecraft.core.component.DataComponents;
|
||||||
|
import net.minecraft.network.chat.Component;
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
import net.minecraft.world.item.ItemStack;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileWriter;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
public final class PackManager {
|
||||||
|
public static final Path EXPORT_DIRECTORY = FabricLoader.getInstance().getGameDir()
|
||||||
|
.resolve("geyser");
|
||||||
|
|
||||||
|
private static final PackManager INSTANCE = new PackManager();
|
||||||
|
|
||||||
|
private String currentPackName;
|
||||||
|
private Path exportPath;
|
||||||
|
private GeyserMappings mappings;
|
||||||
|
|
||||||
|
private PackManager() {}
|
||||||
|
|
||||||
|
public void startPack(String name) throws CommandSyntaxException {
|
||||||
|
if (currentPackName != null) {
|
||||||
|
throw new SimpleCommandExceptionType(Component.literal("Already started a pack with name " + currentPackName)).create();
|
||||||
|
}
|
||||||
|
currentPackName = name;
|
||||||
|
exportPath = createPackDirectory(currentPackName);
|
||||||
|
mappings = new GeyserMappings();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void map(ItemStack stack) throws CommandSyntaxException {
|
||||||
|
ensurePackIsCreated();
|
||||||
|
|
||||||
|
Optional<? extends ResourceLocation> patchedModel = stack.getComponentsPatch().get(DataComponents.ITEM_MODEL);
|
||||||
|
//noinspection OptionalAssignedToNull - annoying Mojang
|
||||||
|
if (patchedModel == null || patchedModel.isEmpty()) {
|
||||||
|
throw new SimpleCommandExceptionType(Component.literal("Item stack does not have a custom model")).create();
|
||||||
|
}
|
||||||
|
|
||||||
|
ResourceLocation model = patchedModel.get();
|
||||||
|
GeyserMapping mapping = new GeyserMapping(model, model,
|
||||||
|
new GeyserMapping.BedrockOptions(Optional.empty(), true, false, 0),
|
||||||
|
stack.getComponentsPatch().split().added()); // TODO removed components
|
||||||
|
mappings.map(stack.getItemHolder(), mapping);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void finish() throws CommandSyntaxException {
|
||||||
|
ensurePackIsCreated();
|
||||||
|
|
||||||
|
JsonElement savedMappings = GeyserMappings.CODEC.encodeStart(JsonOps.INSTANCE, mappings)
|
||||||
|
.getOrThrow(error -> new SimpleCommandExceptionType(Component.literal("Failed to encode Geyser mappings! " + error)).create());
|
||||||
|
|
||||||
|
Gson gson = new GsonBuilder().setPrettyPrinting().create();
|
||||||
|
try {
|
||||||
|
Files.writeString(exportPath.resolve("geyser_mappings.json"), gson.toJson(savedMappings));
|
||||||
|
} catch (IOException exception) {
|
||||||
|
GeyserMappingsGenerator.LOGGER.warn("Failed to write Geyser mappings to pack!", exception);
|
||||||
|
throw new SimpleCommandExceptionType(Component.literal("Failed to write Geyser mappings to pack!")).create();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ensurePackIsCreated() throws CommandSyntaxException {
|
||||||
|
if (currentPackName == null) {
|
||||||
|
throw new SimpleCommandExceptionType(Component.literal("Create a new pack first!")).create();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Path createPackDirectory(String name) throws CommandSyntaxException {
|
||||||
|
Path path = EXPORT_DIRECTORY.resolve(name);
|
||||||
|
if (!Files.isDirectory(path)) {
|
||||||
|
try {
|
||||||
|
Files.createDirectories(path);
|
||||||
|
} catch (IOException exception) {
|
||||||
|
GeyserMappingsGenerator.LOGGER.warn("Failed to create pack export directory!", exception);
|
||||||
|
throw new SimpleCommandExceptionType(Component.literal("Failed to create pack export directory for pack " + name)).create();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static PackManager getInstance() {
|
||||||
|
return INSTANCE;
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user