mirror of
https://github.com/GeyserMC/Geyser.git
synced 2025-12-23 16:59:33 +00:00
Start working on dialog translation
This commit is contained in:
@@ -53,6 +53,7 @@ import org.geysermc.geyser.session.cache.registry.RegistryEntryContext;
|
|||||||
import org.geysermc.geyser.session.cache.registry.RegistryEntryData;
|
import org.geysermc.geyser.session.cache.registry.RegistryEntryData;
|
||||||
import org.geysermc.geyser.session.cache.registry.RegistryUnit;
|
import org.geysermc.geyser.session.cache.registry.RegistryUnit;
|
||||||
import org.geysermc.geyser.session.cache.registry.SimpleJavaRegistry;
|
import org.geysermc.geyser.session.cache.registry.SimpleJavaRegistry;
|
||||||
|
import org.geysermc.geyser.session.dialog.Dialog;
|
||||||
import org.geysermc.geyser.text.ChatDecoration;
|
import org.geysermc.geyser.text.ChatDecoration;
|
||||||
import org.geysermc.geyser.translator.level.BiomeTranslator;
|
import org.geysermc.geyser.translator.level.BiomeTranslator;
|
||||||
import org.geysermc.geyser.util.MinecraftKey;
|
import org.geysermc.geyser.util.MinecraftKey;
|
||||||
@@ -87,6 +88,7 @@ public final class RegistryCache {
|
|||||||
register(JavaRegistries.TRIM_MATERIAL, TrimRecipe::readTrimMaterial);
|
register(JavaRegistries.TRIM_MATERIAL, TrimRecipe::readTrimMaterial);
|
||||||
register(JavaRegistries.TRIM_PATTERN, TrimRecipe::readTrimPattern);
|
register(JavaRegistries.TRIM_PATTERN, TrimRecipe::readTrimPattern);
|
||||||
register(JavaRegistries.DAMAGE_TYPE, RegistryReader.UNIT);
|
register(JavaRegistries.DAMAGE_TYPE, RegistryReader.UNIT);
|
||||||
|
register(JavaRegistries.DIALOG, Dialog::readDialog);
|
||||||
|
|
||||||
register(JavaRegistries.CAT_VARIANT, VariantHolder.reader(CatEntity.BuiltInVariant.class, CatEntity.BuiltInVariant.BLACK));
|
register(JavaRegistries.CAT_VARIANT, VariantHolder.reader(CatEntity.BuiltInVariant.class, CatEntity.BuiltInVariant.BLACK));
|
||||||
register(JavaRegistries.FROG_VARIANT, VariantHolder.reader(FrogEntity.BuiltInVariant.class, FrogEntity.BuiltInVariant.TEMPERATE));
|
register(JavaRegistries.FROG_VARIANT, VariantHolder.reader(FrogEntity.BuiltInVariant.class, FrogEntity.BuiltInVariant.TEMPERATE));
|
||||||
@@ -135,7 +137,11 @@ public final class RegistryCache {
|
|||||||
// Java generic mess - we're sure we're putting the current readers for the correct registry types in the READERS map, so we use raw objects here to let it compile
|
// Java generic mess - we're sure we're putting the current readers for the correct registry types in the READERS map, so we use raw objects here to let it compile
|
||||||
RegistryLoader reader = READERS.get(registryKey);
|
RegistryLoader reader = READERS.get(registryKey);
|
||||||
if (reader != null) {
|
if (reader != null) {
|
||||||
|
try {
|
||||||
reader.load(session, registries.get(registryKey), packet.getEntries());
|
reader.load(session, registries.get(registryKey), packet.getEntries());
|
||||||
|
} catch (Exception exception) {
|
||||||
|
GeyserImpl.getInstance().getLogger().error("Failed parsing registry entries for " + registryKey + "!", exception);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
throw new IllegalStateException("Expected reader for registry " + registryKey);
|
throw new IllegalStateException("Expected reader for registry " + registryKey);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,6 +44,7 @@ import org.geysermc.geyser.level.block.type.Block;
|
|||||||
import org.geysermc.geyser.registry.BlockRegistries;
|
import org.geysermc.geyser.registry.BlockRegistries;
|
||||||
import org.geysermc.geyser.registry.ListRegistry;
|
import org.geysermc.geyser.registry.ListRegistry;
|
||||||
import org.geysermc.geyser.registry.Registries;
|
import org.geysermc.geyser.registry.Registries;
|
||||||
|
import org.geysermc.geyser.session.dialog.Dialog;
|
||||||
import org.geysermc.geyser.util.MinecraftKey;
|
import org.geysermc.geyser.util.MinecraftKey;
|
||||||
import org.geysermc.mcprotocollib.protocol.data.game.chat.ChatType;
|
import org.geysermc.mcprotocollib.protocol.data.game.chat.ChatType;
|
||||||
|
|
||||||
@@ -70,6 +71,7 @@ public class JavaRegistries {
|
|||||||
public static final JavaRegistryKey<TrimMaterial> TRIM_MATERIAL = create("trim_material");
|
public static final JavaRegistryKey<TrimMaterial> TRIM_MATERIAL = create("trim_material");
|
||||||
public static final JavaRegistryKey<TrimPattern> TRIM_PATTERN = create("trim_pattern");
|
public static final JavaRegistryKey<TrimPattern> TRIM_PATTERN = create("trim_pattern");
|
||||||
public static final JavaRegistryKey<RegistryUnit> DAMAGE_TYPE = create("damage_type");
|
public static final JavaRegistryKey<RegistryUnit> DAMAGE_TYPE = create("damage_type");
|
||||||
|
public static final JavaRegistryKey<Dialog> DIALOG = create("dialog");
|
||||||
|
|
||||||
public static final JavaRegistryKey<CatEntity.BuiltInVariant> CAT_VARIANT = create("cat_variant");
|
public static final JavaRegistryKey<CatEntity.BuiltInVariant> CAT_VARIANT = create("cat_variant");
|
||||||
public static final JavaRegistryKey<FrogEntity.BuiltInVariant> FROG_VARIANT = create("frog_variant");
|
public static final JavaRegistryKey<FrogEntity.BuiltInVariant> FROG_VARIANT = create("frog_variant");
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ package org.geysermc.geyser.session.cache.registry;
|
|||||||
|
|
||||||
import net.kyori.adventure.key.Key;
|
import net.kyori.adventure.key.Key;
|
||||||
import org.geysermc.geyser.session.GeyserSession;
|
import org.geysermc.geyser.session.GeyserSession;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Defines a Java registry, which can be hardcoded or data-driven. This class doesn't store registry contents itself, that is handled by {@link org.geysermc.geyser.session.cache.RegistryCache} in the case of
|
* Defines a Java registry, which can be hardcoded or data-driven. This class doesn't store registry contents itself, that is handled by {@link org.geysermc.geyser.session.cache.RegistryCache} in the case of
|
||||||
@@ -81,4 +82,9 @@ public record JavaRegistryKey<T>(Key registryKey, NetworkSerializer<T> networkSe
|
|||||||
|
|
||||||
Key keyFromNetworkId(GeyserSession session, JavaRegistryKey<T> registry, int networkId);
|
Key keyFromNetworkId(GeyserSession session, JavaRegistryKey<T> registry, int networkId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull String toString() {
|
||||||
|
return "Java registry: " + registryKey;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,135 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2025 GeyserMC. http://geysermc.org
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*
|
||||||
|
* @author GeyserMC
|
||||||
|
* @link https://github.com/GeyserMC/Geyser
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.geysermc.geyser.session.dialog;
|
||||||
|
|
||||||
|
import net.kyori.adventure.key.Key;
|
||||||
|
import org.cloudburstmc.nbt.NbtMap;
|
||||||
|
import org.geysermc.cumulus.form.CustomForm;
|
||||||
|
import org.geysermc.geyser.session.GeyserSession;
|
||||||
|
import org.geysermc.geyser.session.cache.registry.RegistryEntryContext;
|
||||||
|
import org.geysermc.geyser.translator.text.MessageTranslator;
|
||||||
|
import org.geysermc.geyser.util.MinecraftKey;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
public abstract class Dialog {
|
||||||
|
|
||||||
|
private static final Key PLAIN_MESSAGE_BODY = MinecraftKey.key("plain_message");
|
||||||
|
|
||||||
|
private final String title;
|
||||||
|
private final String externalTitle;
|
||||||
|
private final AfterAction afterAction;
|
||||||
|
private final List<String> labels;
|
||||||
|
private final List<Object> inputs; // TODO
|
||||||
|
|
||||||
|
protected Dialog(GeyserSession session, NbtMap map) {
|
||||||
|
title = MessageTranslator.convertFromNullableNbtTag(session, map.get("title"));
|
||||||
|
externalTitle = MessageTranslator.convertFromNullableNbtTag(session, map.get("title"));
|
||||||
|
afterAction = AfterAction.fromString(map.getString("after_action"));
|
||||||
|
|
||||||
|
Object bodyTag = map.get("body");
|
||||||
|
if (bodyTag == null) {
|
||||||
|
labels = List.of();
|
||||||
|
} else if (bodyTag instanceof NbtMap bodyMap) {
|
||||||
|
String body = readBody(session, bodyMap);
|
||||||
|
if (body != null) {
|
||||||
|
labels = List.of(body);
|
||||||
|
} else {
|
||||||
|
labels = List.of();
|
||||||
|
}
|
||||||
|
} else if (bodyTag instanceof List<?> bodyList) {
|
||||||
|
List<String> bodies = new ArrayList<>();
|
||||||
|
for (Object tag : bodyList) {
|
||||||
|
if (tag instanceof NbtMap bodyMap) {
|
||||||
|
String body = readBody(session, bodyMap);
|
||||||
|
if (body != null) {
|
||||||
|
bodies.add(body);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new IllegalStateException("Found non-NBT map in list of bodies, was: " + tag);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
labels = List.copyOf(bodies);
|
||||||
|
} else {
|
||||||
|
throw new IllegalStateException("Expected body tag to either be a NBT map or list thereof, was: " + bodyTag);
|
||||||
|
}
|
||||||
|
|
||||||
|
inputs = List.of();
|
||||||
|
}
|
||||||
|
|
||||||
|
public CustomForm buildForm() {
|
||||||
|
return createForm().build();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected CustomForm.Builder createForm() {
|
||||||
|
CustomForm.Builder builder = CustomForm.builder()
|
||||||
|
.title(title);
|
||||||
|
for (String label : labels) {
|
||||||
|
builder.label(label);
|
||||||
|
}
|
||||||
|
return builder;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String readBody(GeyserSession session, NbtMap tag) {
|
||||||
|
Key type = MinecraftKey.key(tag.getString("type"));
|
||||||
|
if (type.equals(PLAIN_MESSAGE_BODY)) {
|
||||||
|
return MessageTranslator.convertFromNullableNbtTag(session, tag.get("contents"));
|
||||||
|
}
|
||||||
|
// Other type is item, can't display that in forms
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Dialog readDialog(RegistryEntryContext context) {
|
||||||
|
return readDialog(context.session(), context.data());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Dialog readDialog(GeyserSession session, NbtMap map) {
|
||||||
|
// TYPES: notice, server_links, dialog_list, multi_action, confirmation
|
||||||
|
Key type = MinecraftKey.key(map.getString("type"));
|
||||||
|
if (type.equals(NoticeDialog.TYPE)) {
|
||||||
|
return new NoticeDialog(session, map);
|
||||||
|
}
|
||||||
|
return new Dialog(session, map) {};
|
||||||
|
// throw new UnsupportedOperationException("Unable to read unknown dialog type " + type + "!"); // TODO put this here once all types are implemented
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum AfterAction {
|
||||||
|
CLOSE,
|
||||||
|
NONE,
|
||||||
|
WAIT_FOR_RESPONSE;
|
||||||
|
|
||||||
|
public static AfterAction fromString(String string) {
|
||||||
|
for (AfterAction action : values()) {
|
||||||
|
if (action.name().toLowerCase(Locale.ROOT).equals(string)) {
|
||||||
|
return action;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2025 GeyserMC. http://geysermc.org
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*
|
||||||
|
* @author GeyserMC
|
||||||
|
* @link https://github.com/GeyserMC/Geyser
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.geysermc.geyser.session.dialog;
|
||||||
|
|
||||||
|
import net.kyori.adventure.key.Key;
|
||||||
|
import org.cloudburstmc.nbt.NbtMap;
|
||||||
|
import org.geysermc.geyser.session.GeyserSession;
|
||||||
|
import org.geysermc.geyser.util.MinecraftKey;
|
||||||
|
|
||||||
|
public class NoticeDialog extends Dialog {
|
||||||
|
|
||||||
|
public static final Key TYPE = MinecraftKey.key("notice");
|
||||||
|
|
||||||
|
public NoticeDialog(GeyserSession session, NbtMap map) {
|
||||||
|
super(session, map);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -26,6 +26,8 @@
|
|||||||
package org.geysermc.geyser.translator.protocol.java.dialogues;
|
package org.geysermc.geyser.translator.protocol.java.dialogues;
|
||||||
|
|
||||||
import org.geysermc.geyser.session.GeyserSession;
|
import org.geysermc.geyser.session.GeyserSession;
|
||||||
|
import org.geysermc.geyser.session.cache.registry.JavaRegistries;
|
||||||
|
import org.geysermc.geyser.session.dialog.Dialog;
|
||||||
import org.geysermc.geyser.translator.protocol.PacketTranslator;
|
import org.geysermc.geyser.translator.protocol.PacketTranslator;
|
||||||
import org.geysermc.geyser.translator.protocol.Translator;
|
import org.geysermc.geyser.translator.protocol.Translator;
|
||||||
import org.geysermc.mcprotocollib.protocol.packet.ingame.clientbound.ClientboundShowDialogGamePacket;
|
import org.geysermc.mcprotocollib.protocol.packet.ingame.clientbound.ClientboundShowDialogGamePacket;
|
||||||
@@ -35,6 +37,12 @@ public class JavaShowDialogGameTranslator extends PacketTranslator<ClientboundSh
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void translate(GeyserSession session, ClientboundShowDialogGamePacket packet) {
|
public void translate(GeyserSession session, ClientboundShowDialogGamePacket packet) {
|
||||||
|
Dialog dialog;
|
||||||
|
if (packet.getDialog().isId()) {
|
||||||
|
dialog = JavaRegistries.DIALOG.fromNetworkId(session, packet.getDialog().id());
|
||||||
|
} else {
|
||||||
|
dialog = Dialog.readDialog(session, packet.getDialog().custom());
|
||||||
|
}
|
||||||
|
session.sendForm(dialog.buildForm());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
|
|||||||
import net.kyori.adventure.text.serializer.legacy.CharacterAndFormat;
|
import net.kyori.adventure.text.serializer.legacy.CharacterAndFormat;
|
||||||
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
|
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
|
||||||
import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer;
|
import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer;
|
||||||
|
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||||
import org.cloudburstmc.nbt.NbtMap;
|
import org.cloudburstmc.nbt.NbtMap;
|
||||||
import org.cloudburstmc.protocol.bedrock.packet.TextPacket;
|
import org.cloudburstmc.protocol.bedrock.packet.TextPacket;
|
||||||
import org.geysermc.geyser.GeyserImpl;
|
import org.geysermc.geyser.GeyserImpl;
|
||||||
@@ -474,6 +475,14 @@ public class MessageTranslator {
|
|||||||
return convertMessageForTooltip(parsed, session.locale());
|
return convertMessageForTooltip(parsed, session.locale());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public static String convertFromNullableNbtTag(GeyserSession session, @Nullable Object nbtTag) {
|
||||||
|
if (nbtTag == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return convertMessage(session, componentFromNbtTag(nbtTag));
|
||||||
|
}
|
||||||
|
|
||||||
public static Component componentFromNbtTag(Object nbtTag) {
|
public static Component componentFromNbtTag(Object nbtTag) {
|
||||||
return componentFromNbtTag(nbtTag, Style.empty());
|
return componentFromNbtTag(nbtTag, Style.empty());
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user