Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve (Multiblock) Server Side Number Formatting #1670

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package gregtech.api.gui.translation;

import java.lang.reflect.Type;

import javax.annotation.Nullable;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonElement;
import com.google.gson.JsonParseException;

import net.minecraft.util.EnumTypeAdapterFactory;
import net.minecraft.util.JsonUtils;
import net.minecraft.util.text.ITextComponent;
import net.minecraft.util.text.Style;
import net.minecraft.util.text.TextComponentTranslation;

public class EnhancedTextComponentSerializer extends ITextComponent.Serializer {

private static final Gson GSON;

@Override
public ITextComponent deserialize(final JsonElement element, final Type type, final JsonDeserializationContext context) throws JsonParseException {
final ITextComponent superResult = super.deserialize(element, type, context);
if (!(superResult instanceof TextComponentTranslation)) {
return superResult;
}
final TextComponentTranslation original = (TextComponentTranslation) superResult;
final EnhancedTextComponentTranslation result = new EnhancedTextComponentTranslation(original.getKey(), original.getFormatArgs());
for (ITextComponent sibling : original.getSiblings()) {
result.appendSibling(sibling);
}
return result;
}

/**
* Serializes a component into JSON.
*/
public static String componentToJson(final ITextComponent component) {
return GSON.toJson(component);
}

/**
* Parses a JSON string into a {@link ITextComponent}, with strict parsing.
*
* @see #fromJsonLenient(String)
* @see {@link com.google.gson.stream.JsonReader#setLenient(boolean)}
*/
@Nullable
public static ITextComponent jsonToComponent(final String json) {
return JsonUtils.gsonDeserialize(GSON, json, ITextComponent.class, false);
}

/**
* Parses a JSON string into a {@link ITextComponent}, being lenient upon parse
* errors.
*
* @see #jsonToComponent(String)
* @see {@link com.google.gson.stream.JsonReader#setLenient(boolean)}
*/
@Nullable
public static ITextComponent fromJsonLenient(final String json) {
return JsonUtils.gsonDeserialize(GSON, json, ITextComponent.class, true);
}

static {
GsonBuilder gsonbuilder = new GsonBuilder();
gsonbuilder.registerTypeHierarchyAdapter(ITextComponent.class, new EnhancedTextComponentSerializer());
gsonbuilder.registerTypeHierarchyAdapter(Style.class, new Style.Serializer());
gsonbuilder.registerTypeAdapterFactory(new EnumTypeAdapterFactory());
GSON = gsonbuilder.create();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
package gregtech.api.gui.translation;

import java.util.IllegalFormatException;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import net.minecraft.util.text.ITextComponent;
import net.minecraft.util.text.TextComponentString;
import net.minecraft.util.text.TextComponentTranslation;
import net.minecraft.util.text.TextComponentTranslationFormatException;
import net.minecraftforge.fml.common.ObfuscationReflectionHelper;

public class EnhancedTextComponentTranslation extends TextComponentTranslation {

public static final Pattern ENHANCED_STRING_VARIABLE_PATTERN = Pattern.compile("%(\\\\d+\\\\$)?([-#+ 0,(\\\\<]*)?(\\\\d+)?(\\\\.\\\\d+)?([tT])?([a-zA-Z%])");

private final List<ITextComponent> copyChildren;

public EnhancedTextComponentTranslation(final String translationKey, final Object... args) {
super(translationKey, args);
this.copyChildren = ObfuscationReflectionHelper.getPrivateValue(TextComponentTranslation.class, this, "children");
}

@Override
protected void initializeFromFormat(final String format) {
final Matcher matcher = ENHANCED_STRING_VARIABLE_PATTERN.matcher(format);
int argIndex = 0;
int start = 0;
Object[] formatArgs = getFormatArgs();

try {
int end;

for (; matcher.find(start); start = end) {
final int found = matcher.start();
end = matcher.end();

// Text before the first %
if (found > start) {
final TextComponentString text = new TextComponentString(String.format(format.substring(start, found)));
text.getStyle().setParentStyle(this.getStyle());
this.copyChildren.add(text);
}

final String formatOption = matcher.group(6);
final String string = format.substring(found, end);

// Escape sequence %%
if ("%".equals(formatOption) && "%%".equals(string)) {
final TextComponentString text = new TextComponentString("%");
text.getStyle().setParentStyle(this.getStyle());
this.copyChildren.add(text);
} else {
// Argument number specifier
final String arg = matcher.group(1);
final int index = arg != null ? Integer.parseInt(arg) - 1 : argIndex++;

if (index < formatArgs.length) {
final Object object = formatArgs[index];
final ITextComponent text;
if (object == null) {
text = new TextComponentString("null");
}
else if (object instanceof ITextComponent) {
text = (ITextComponent) object;
} else {
// Concatenate all the format options except the index specifier
final StringBuilder builder = new StringBuilder();
builder.append('%');
// groupCount doesn't include group 0 hence <=
for (int g = 2; g <= matcher.groupCount(); ++g) {
final String group = matcher.group(g);
if (group != null) {
builder.append(group);
}
}
text = new TextComponentString(String.format(builder.toString(), fixArg(formatOption, object)));
}
text.getStyle().setParentStyle(this.getStyle());
this.copyChildren.add(text);
}
}
}

// Trailing text
if (start < format.length()) {
final TextComponentString text = new TextComponentString(String.format(format.substring(start)));
text.getStyle().setParentStyle(this.getStyle());
this.copyChildren.add(text);
}
} catch (IllegalFormatException | NumberFormatException e) {
throw new TextComponentTranslationFormatException(this, e);
}
}

private static Object fixArg(final String formatOption, final Object original) {
if (original instanceof String) {
final String string = (String) original;
// Short circuit common case
if ("s".equals(formatOption)) {
return string;
}
if ("d".equals(formatOption) || "o".equals(formatOption) || "x".equalsIgnoreCase(formatOption)) {
return Long.valueOf(string);
}
if ("e".equalsIgnoreCase(formatOption) || "f".equals(formatOption) || "g".equalsIgnoreCase(formatOption) || "a".equalsIgnoreCase(formatOption)) {
return Double.valueOf(string);
}
}
return original;
}

@Override
public EnhancedTextComponentTranslation createCopy() {
final Object[] formatArgs = getFormatArgs();
final Object[] copyArgs = new Object[formatArgs.length];

for (int i = 0; i < formatArgs.length; ++i) {
if (formatArgs[i] instanceof ITextComponent) {
copyArgs[i] = ((ITextComponent) formatArgs[i]).createCopy();
} else {
copyArgs[i] = formatArgs[i];
}
}

final EnhancedTextComponentTranslation copy = new EnhancedTextComponentTranslation(getKey(), copyArgs);
copy.setStyle(this.getStyle().createShallowCopy());

for (ITextComponent sibling : this.getSiblings()) {
copy.appendSibling(sibling.createCopy());
}
return copy;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

import gregtech.api.gui.IRenderContext;
import gregtech.api.gui.Widget;
import gregtech.api.gui.translation.EnhancedTextComponentSerializer;
import gregtech.api.gui.translation.EnhancedTextComponentTranslation;
import gregtech.api.util.Position;
import gregtech.api.util.Size;
import net.minecraft.client.Minecraft;
Expand Down Expand Up @@ -113,7 +115,7 @@ public void detectAndSendChanges() {
writeUpdateInfo(1, buffer -> {
buffer.writeVarInt(displayText.size());
for (ITextComponent textComponent : displayText) {
buffer.writeString(ITextComponent.Serializer.componentToJson(textComponent));
buffer.writeString(EnhancedTextComponentSerializer.componentToJson(textComponent));
}
});
}
Expand Down Expand Up @@ -169,7 +171,7 @@ public void readUpdateInfo(int id, PacketBuffer buffer) {
int count = buffer.readVarInt();
for (int i = 0; i < count; i++) {
String jsonText = buffer.readString(32767);
this.displayText.add(ITextComponent.Serializer.jsonToComponent(jsonText));
this.displayText.add(EnhancedTextComponentSerializer.jsonToComponent(jsonText));
}
formatDisplayText();
updateComponentTextSize();
Expand Down
2 changes: 1 addition & 1 deletion src/main/resources/assets/gregtech/lang/en_us.lang
Original file line number Diff line number Diff line change
Expand Up @@ -2961,7 +2961,7 @@ gregtech.multiblock.invalid_structure=Invalid structure.
gregtech.multiblock.invalid_structure.tooltip=This block is a controller of the multiblock structure. For building help, see structure template in JEI
gregtech.multiblock.validation_failed=Invalid amount of inputs/outputs.
gregtech.multiblock.max_energy_per_tick=Max EU/t: %s (%s)
gregtech.multiblock.generation_eu=Outputting: %s EU/t
gregtech.multiblock.generation_eu=Outputting: %,d EU/t
gregtech.multiblock.preview.tilt=Shift+LMB to tilt
gregtech.multiblock.preview.zoom=Shift+RMB to zoom
gregtech.multiblock.preview.pan=LMB+Drag to pan
Expand Down