Add custom usercontent loading from json and javascript files alongside bugfixes for page list, refactorings and documentation

This commit is contained in:
Marcel Konrad
2019-11-02 18:41:15 +01:00
parent 677f899661
commit fc70c82545
126 changed files with 3664 additions and 656 deletions

View File

@@ -0,0 +1,69 @@
package exopandora.worldhandler.usercontent;
import javax.annotation.Nullable;
import javax.script.Invocable;
import javax.script.ScriptEngine;
import javax.script.ScriptException;
import exopandora.worldhandler.WorldHandler;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
@OnlyIn(Dist.CLIENT)
public class ScriptEngineAdapter
{
private final ScriptEngine engine;
public ScriptEngineAdapter(ScriptEngine engine)
{
this.engine = engine;
}
@Nullable
public Object invokeFunction(String function)
{
return this.invokeFunction(function, null);
}
@Nullable
public Object invokeFunction(String function, Object value)
{
if(function != null && !function.isEmpty())
{
try
{
if(value != null)
{
return ((Invocable) this.engine).invokeFunction(function, value);
}
else
{
return ((Invocable) this.engine).invokeFunction(function);
}
}
catch(Exception e)
{
e.printStackTrace();
}
}
else
{
WorldHandler.LOGGER.warn("No function specified");
}
return null;
}
public void addObject(String key, Object instance)
{
this.engine.put(key, instance);
}
public void eval(String js) throws ScriptException
{
if(js != null)
{
this.engine.eval(js);
}
}
}

View File

@@ -0,0 +1,64 @@
package exopandora.worldhandler.usercontent;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.annotation.Nullable;
import exopandora.worldhandler.builder.impl.BuilderUsercontent;
import net.minecraft.client.Minecraft;
import net.minecraft.util.text.ChatType;
import net.minecraft.util.text.StringTextComponent;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
@OnlyIn(Dist.CLIENT)
public class UsercontentAPI
{
private final Map<String, String> values = new HashMap<String, String>();
private final List<BuilderUsercontent> builders;
public UsercontentAPI(List<BuilderUsercontent> builders)
{
this.builders = builders;
}
@Nullable
public String getValue(String id)
{
return this.values.get(id);
}
public void updateValue(String id, String value)
{
this.values.put(id, value);
}
public void addChatMessage(Object object)
{
if(object != null)
{
Minecraft.getInstance().ingameGUI.addChatMessage(ChatType.CHAT, new StringTextComponent(object.toString()));
}
}
public void setCommandArgument(int command, int index, String object)
{
if(command < this.builders.size() && command >= 0)
{
this.builders.get(command).set(index, object);
}
}
@Nullable
public String getCommandArgument(int command, int index)
{
if(command < this.builders.size() && command >= 0)
{
return this.builders.get(command).get(index);
}
return null;
}
}

View File

@@ -0,0 +1,84 @@
package exopandora.worldhandler.usercontent;
import java.nio.file.Path;
import javax.script.ScriptEngine;
import exopandora.worldhandler.usercontent.model.JsonUsercontent;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
@OnlyIn(Dist.CLIENT)
public class UsercontentConfig
{
private final String id;
private final JsonUsercontent content;
private final String js;
private final ScriptEngine engine;
private UsercontentConfig(Builder builder)
{
this.id = builder.id;
this.content = builder.content;
this.js = builder.js;
this.engine = builder.engine;
}
public String getId()
{
return this.id;
}
public JsonUsercontent getContent()
{
return this.content;
}
public String getJs()
{
return this.js;
}
public ScriptEngine getScriptEngine()
{
return this.engine;
}
@OnlyIn(Dist.CLIENT)
public static class Builder
{
private final String id;
private JsonUsercontent content;
private String js;
private ScriptEngine engine;
public Builder(Path path)
{
String fileName = path.getFileName().toString();
this.id = fileName.substring(0, fileName.length() - 3);
}
public Builder setContent(JsonUsercontent content)
{
this.content = content;
return this;
}
public Builder setJs(String js)
{
this.js = js;
return this;
}
public Builder setScriptEngine(ScriptEngine engine)
{
this.engine = engine;
return this;
}
public UsercontentConfig build()
{
return new UsercontentConfig(this);
}
}
}

View File

@@ -0,0 +1,189 @@
package exopandora.worldhandler.usercontent;
import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import javax.script.ScriptContext;
import javax.script.ScriptEngine;
import org.apache.commons.io.IOUtils;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonSyntaxException;
import com.google.gson.TypeAdapter;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter;
import exopandora.worldhandler.WorldHandler;
import exopandora.worldhandler.builder.types.ArgumentType;
import exopandora.worldhandler.gui.button.EnumIcon;
import exopandora.worldhandler.usercontent.model.Action;
import exopandora.worldhandler.usercontent.model.BooleanExpression;
import exopandora.worldhandler.usercontent.model.JsonButton;
import exopandora.worldhandler.usercontent.model.JsonElement;
import exopandora.worldhandler.usercontent.model.JsonUsercontent;
import jdk.nashorn.api.scripting.NashornScriptEngineFactory;
import net.minecraft.util.ResourceLocation;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
@OnlyIn(Dist.CLIENT)
public class UsercontentLoader
{
public static final List<UsercontentConfig> CONFIGS = new ArrayList<UsercontentConfig>();
public static void load(Path path)
{
try
{
UsercontentLoader.load0(path);
}
catch(IOException e)
{
e.printStackTrace();
}
}
public static void load0(Path path) throws IOException
{
if(Files.notExists(path) || !Files.isReadable(path))
{
throw new IOException("Path is not accessible");
}
final Gson gson = new GsonBuilder()
.registerTypeAdapter(ArgumentType.class, new EnumTypeAdapter<ArgumentType>(ArgumentType.class))
.registerTypeAdapter(EnumIcon.class, new EnumTypeAdapter<EnumIcon>(EnumIcon.class))
.registerTypeAdapter(BooleanExpression.Type.class, new EnumTypeAdapter<BooleanExpression.Type>(BooleanExpression.Type.class))
.registerTypeAdapter(JsonButton.Type.class, new EnumTypeAdapter<JsonButton.Type>(JsonButton.Type.class))
.registerTypeAdapter(Action.Type.class, new EnumTypeAdapter<Action.Type>(Action.Type.class))
.registerTypeAdapter(JsonElement.Type.class, new EnumTypeAdapter<JsonElement.Type>(JsonElement.Type.class))
.create();
final List<Path> folders = Files.list(path)
.filter(Files::isDirectory)
.filter(Files::isReadable)
.filter(UsercontentLoader::isValidPathName)
.collect(Collectors.toList());
for(Path folder : folders)
{
Optional<Path> json = UsercontentLoader.locateFile(folder, "json");
Optional<Path> js = UsercontentLoader.locateFile(folder, "js");
if(json.isPresent())
{
UsercontentConfig.Builder builder = new UsercontentConfig.Builder(js.get());
String usercontent = UsercontentLoader.readFile(json.get());
try
{
JsonUsercontent content = gson.fromJson(usercontent, JsonUsercontent.class);
content.validate();
builder.setContent(content);
if(js.isPresent())
{
builder.setScriptEngine(UsercontentLoader.buildScriptEngine());
builder.setJs(UsercontentLoader.readFile(js.get()));
}
UsercontentLoader.CONFIGS.add(builder.build());
}
catch(JsonSyntaxException | IllegalStateException e)
{
WorldHandler.LOGGER.error("Error loading usercontent " + json.get().toAbsolutePath());
WorldHandler.LOGGER.throwing(e);
}
}
}
}
private static final List<String> ALLOWED_CLASSES = Arrays.asList
(
"exopandora.worldhandler.helper.ActionHelper"
);
private static ScriptEngine buildScriptEngine()
{
final NashornScriptEngineFactory factory = new NashornScriptEngineFactory();
final ScriptEngine engine = factory.getScriptEngine(s -> ALLOWED_CLASSES.stream().anyMatch(s::equals));
final ScriptContext context = engine.getContext();
context.removeAttribute("load", context.getAttributesScope("load"));
context.removeAttribute("quit", context.getAttributesScope("quit"));
context.removeAttribute("loadWithNewGlobal", context.getAttributesScope("loadWithNewGlobal"));
context.removeAttribute("exit", context.getAttributesScope("exit"));
return engine;
}
private static Optional<Path> locateFile(Path path, String fileExtension)
{
Path json = path.resolve(path.getFileName().toString() + "." + fileExtension);
if(Files.exists(json) && Files.isRegularFile(json) && Files.isReadable(json))
{
return Optional.of(json);
}
return Optional.empty();
}
private static String readFile(Path path)
{
try
{
return IOUtils.toString(path.toUri(), Charset.defaultCharset());
}
catch(IOException e)
{
e.printStackTrace();
}
return null;
}
private static boolean isValidPathName(Path path)
{
String name = path.getFileName().toString();
boolean valid = ResourceLocation.isResouceNameValid(name);
if(!valid)
{
WorldHandler.LOGGER.error("Invalid usercontent folder name: " + name);
}
return valid;
}
@OnlyIn(Dist.CLIENT)
public static class EnumTypeAdapter<T extends Enum<T>> extends TypeAdapter<T>
{
private final Class<T> klass;
public EnumTypeAdapter(Class<T> klass)
{
this.klass = klass;
}
@Override
public void write(JsonWriter writer, T value) throws IOException
{
writer.value(value.name().toLowerCase());
}
@Override
public T read(JsonReader reader) throws IOException
{
return Enum.valueOf(this.klass, reader.nextString().toUpperCase());
}
}
}

View File

@@ -0,0 +1,33 @@
package exopandora.worldhandler.usercontent;
import exopandora.worldhandler.usercontent.model.BooleanExpression;
import exopandora.worldhandler.usercontent.model.JsonButton;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
@OnlyIn(Dist.CLIENT)
public class VisibleActiveObject<T> extends VisibleObject<T>
{
private final BooleanExpression active;
public VisibleActiveObject(BooleanExpression visible, BooleanExpression active, T object)
{
super(visible, object);
this.active = active;
}
public VisibleActiveObject(JsonButton button, T object)
{
this(button.getAttributes() != null ? button.getAttributes().getVisible() : null, button.getAttributes() != null ? button.getAttributes().getEnabled() : null, object);
}
public boolean isEnabled(ScriptEngineAdapter engine)
{
if(this.active != null)
{
return this.active.eval(engine);
}
return true;
}
}

View File

@@ -0,0 +1,33 @@
package exopandora.worldhandler.usercontent;
import exopandora.worldhandler.usercontent.model.BooleanExpression;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
@OnlyIn(Dist.CLIENT)
public class VisibleObject<T>
{
private final BooleanExpression visible;
private final T object;
public VisibleObject(BooleanExpression visible, T object)
{
this.visible = visible;
this.object = object;
}
public boolean isVisible(ScriptEngineAdapter engine)
{
if(this.visible != null)
{
return this.visible.eval(engine);
}
return true;
}
public T getObject()
{
return this.object;
}
}

View File

@@ -0,0 +1,136 @@
package exopandora.worldhandler.usercontent.factory;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.Function;
import javax.annotation.Nullable;
import exopandora.worldhandler.builder.impl.BuilderUsercontent;
import exopandora.worldhandler.gui.content.Content;
import exopandora.worldhandler.helper.ActionHelper;
import exopandora.worldhandler.helper.CommandHelper;
import exopandora.worldhandler.usercontent.ScriptEngineAdapter;
import exopandora.worldhandler.usercontent.UsercontentAPI;
import exopandora.worldhandler.usercontent.VisibleObject;
import exopandora.worldhandler.usercontent.model.Action;
import exopandora.worldhandler.util.ActionHandler;
import net.minecraft.client.Minecraft;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
@OnlyIn(Dist.CLIENT)
public class ActionHandlerFactory
{
private final UsercontentAPI api;
private final List<VisibleObject<BuilderUsercontent>> builders;
private final ScriptEngineAdapter engine;
public ActionHandlerFactory(UsercontentAPI api, List<VisibleObject<BuilderUsercontent>> builders, ScriptEngineAdapter engine)
{
this.api = api;
this.builders = builders;
this.engine = engine;
}
@Nullable
public ActionHandler createActionHandler(Content content, Action action)
{
return this.createActionHandler(content, action, null);
}
@Nullable
public ActionHandler createActionHandler(Content content, Action action, String value)
{
if(action == null)
{
return null;
}
else if(Action.Type.OPEN.equals(action.getType()))
{
if(action.getAttributes() != null && action.getAttributes().getValue() != null)
{
return () -> ActionHelper.open(action.getAttributes().getValue());
}
}
else if(Action.Type.SET.equals(action.getType()))
{
if(action.getAttributes() != null && (action.getAttributes().getValue() != null || value != null))
{
return () ->
{
VisibleObject<BuilderUsercontent> visObj = this.builders.get(action.getAttributes().getCommand());
if(visObj != null && visObj.getObject() != null)
{
visObj.getObject().set(action.getAttributes().getIndex(), value != null ? value : action.getAttributes().getValue());
}
};
}
}
else if(Action.Type.RUN.equals(action.getType()))
{
if(action.getAttributes() != null)
{
return () ->
{
if(action.getAttributes().getValue() == null)
{
CommandHelper.sendCommand(this.builders.get(action.getAttributes().getCommand()).getObject());
}
else if(!action.getAttributes().getValue().isEmpty())
{
Minecraft.getInstance().player.sendChatMessage(action.getAttributes().getValue());
}
};
}
}
else if(Action.Type.BACK.equals(action.getType()))
{
return () -> ActionHelper.back(content);
}
else if(Action.Type.BACK_TO_GAME.equals(action.getType()))
{
return ActionHelper::backToGame;
}
else if(Action.Type.JS.equals(action.getType()))
{
if(action.getAttributes() != null && action.getAttributes().getFunction() != null && !action.getAttributes().getFunction().isEmpty())
{
return () -> this.engine.invokeFunction(action.getAttributes().getFunction(), value != null ? value : action.getAttributes().getValue());
}
}
return null;
}
public <T> Consumer<T> createResponder(Function<T, String> toStringMapper, String id, Action action)
{
if(Action.Type.SET.equals(action.getType()))
{
if(action.getAttributes() != null)
{
return string ->
{
String value = toStringMapper.apply(string);
this.api.updateValue(id, value);
this.builders.get(action.getAttributes().getCommand()).getObject().set(action.getAttributes().getIndex(), value);
};
}
}
else if(Action.Type.JS.equals(action.getType()))
{
if(action.getAttributes() != null && action.getAttributes().getFunction() != null)
{
return string ->
{
String value = toStringMapper.apply(string);
this.api.updateValue(id, value);
this.engine.invokeFunction(action.getAttributes().getFunction(), value);
};
}
}
return string -> this.api.updateValue(id, toStringMapper.apply(string));
}
}

View File

@@ -0,0 +1,125 @@
package exopandora.worldhandler.usercontent.factory;
import java.util.function.Consumer;
import javax.annotation.Nullable;
import com.google.common.base.Predicates;
import exopandora.worldhandler.gui.button.GuiButtonIcon;
import exopandora.worldhandler.gui.button.GuiButtonItem;
import exopandora.worldhandler.gui.button.GuiButtonList;
import exopandora.worldhandler.gui.button.GuiButtonTooltip;
import exopandora.worldhandler.gui.button.GuiSlider;
import exopandora.worldhandler.gui.button.GuiTextFieldTooltip;
import exopandora.worldhandler.gui.container.Container;
import exopandora.worldhandler.gui.content.Content;
import exopandora.worldhandler.gui.logic.LogicSliderSimple;
import exopandora.worldhandler.usercontent.UsercontentAPI;
import exopandora.worldhandler.usercontent.model.JsonItem;
import exopandora.worldhandler.util.TextFormatting;
import exopandora.worldhandler.usercontent.model.JsonButton;
import net.minecraft.client.gui.widget.Widget;
import net.minecraft.util.ResourceLocation;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import net.minecraftforge.registries.ForgeRegistries;
@OnlyIn(Dist.CLIENT)
public class ButtonFactory extends WidgetFactory
{
public ButtonFactory(UsercontentAPI api, ActionHandlerFactory actionHandlerFactory)
{
super(api, actionHandlerFactory);
}
@Nullable
public Widget createButton(JsonButton button, Content content, Container container, int x, int y)
{
if(JsonButton.Type.BUTTON.equals(button.getType()))
{
return new GuiButtonTooltip
(
button.getDimensions().getX() + x,
button.getDimensions().getY() + y,
button.getDimensions().getWidth(),
button.getDimensions().getHeight(),
TextFormatting.formatNonnull(button.getText()),
TextFormatting.formatNullable(button.getAttributes() != null ? button.getAttributes().getTooltip() : null),
this.getActionHandlerFactory().createActionHandler(content, button.getAction())
);
}
else if(JsonButton.Type.ITEM_BUTTON.equals(button.getType()))
{
return new GuiButtonItem
(
button.getDimensions().getX() + x,
button.getDimensions().getY() + y,
button.getDimensions().getWidth(),
button.getDimensions().getHeight(),
ForgeRegistries.ITEMS.getValue(new ResourceLocation(button.getAttributes().getItem())),
this.getActionHandlerFactory().createActionHandler(content, button.getAction())
);
}
else if(JsonButton.Type.ICON_BUTTON.equals(button.getType()))
{
return new GuiButtonIcon
(
button.getDimensions().getX() + x,
button.getDimensions().getY() + y,
button.getDimensions().getWidth(),
button.getDimensions().getHeight(),
button.getAttributes().getIcon(),
TextFormatting.formatNonnull(button.getAttributes().getTooltip()),
this.getActionHandlerFactory().createActionHandler(content, button.getAction())
);
}
else if(JsonButton.Type.LIST_BUTTON.equals(button.getType()))
{
return new GuiButtonList<JsonItem>
(
button.getDimensions().getX() + x,
button.getDimensions().getY() + y,
button.getAttributes().getItems(),
button.getDimensions().getWidth(),
button.getDimensions().getHeight(),
container,
new UsercontentLogicMapped<JsonButton.Type>(this.getApi(), this.getActionHandlerFactory(), content, button)
);
}
else if(JsonButton.Type.SLIDER.equals(button.getType()))
{
Consumer<Integer> responder = this.getActionHandlerFactory().createResponder(integer -> integer.toString(), button.getAttributes().getId(), button.getAction());
return new GuiSlider
(
button.getDimensions().getX() + x,
button.getDimensions().getY() + y,
button.getDimensions().getWidth(),
button.getDimensions().getHeight(),
button.getAttributes().getMin(),
button.getAttributes().getMax(),
button.getAttributes().getStart(),
container,
new LogicSliderSimple(button.getAttributes().getId(), TextFormatting.formatNullable(button.getText()), responder)
);
}
else if(JsonButton.Type.TEXTFIELD.equals(button.getType()))
{
GuiTextFieldTooltip textfield = new GuiTextFieldTooltip
(
button.getDimensions().getX() + x,
button.getDimensions().getY() + y,
button.getDimensions().getWidth(),
button.getDimensions().getHeight(),
TextFormatting.formatNullable(button.getText())
);
textfield.setValidator(Predicates.notNull());
textfield.setText(this.getApi().getValue(button.getAttributes().getId()));
textfield.setResponder(this.getActionHandlerFactory().createResponder(string -> textfield.getText(), button.getAttributes().getId(), button.getAction()));
return textfield;
}
return null;
}
}

View File

@@ -0,0 +1,79 @@
package exopandora.worldhandler.usercontent.factory;
import javax.annotation.Nullable;
import exopandora.worldhandler.gui.button.GuiButtonBase;
import exopandora.worldhandler.gui.button.GuiButtonTooltip;
import exopandora.worldhandler.gui.container.Container;
import exopandora.worldhandler.gui.content.Content;
import exopandora.worldhandler.gui.element.Element;
import exopandora.worldhandler.gui.element.impl.ElementPageList;
import exopandora.worldhandler.gui.logic.ILogicPageList;
import exopandora.worldhandler.usercontent.UsercontentAPI;
import exopandora.worldhandler.usercontent.model.JsonElement;
import exopandora.worldhandler.usercontent.model.JsonItem;
import exopandora.worldhandler.usercontent.model.JsonWidget;
import exopandora.worldhandler.util.ActionHandler;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
@OnlyIn(Dist.CLIENT)
public class ElementFactory extends WidgetFactory
{
public ElementFactory(UsercontentAPI api, ActionHandlerFactory actionHandlerFactory)
{
super(api, actionHandlerFactory);
}
@Nullable
public Element createElement(JsonElement element, Content content, Container container, int x, int y)
{
if(JsonElement.Type.PAGE_LIST.equals(element.getType()))
{
return new ElementPageList<JsonItem>
(
element.getDimensions().getX() + x,
element.getDimensions().getY() + y,
element.getAttributes().getItems(),
element.getDimensions().getWidth(),
element.getDimensions().getHeight(),
element.getAttributes().getLength(),
container,
new UsercontentLogicPageList<JsonElement.Type>(this.getApi(), this.getActionHandlerFactory(), content, container, element)
);
}
return null;
}
@OnlyIn(Dist.CLIENT)
public static class UsercontentLogicPageList<T extends Enum<T>> extends UsercontentLogicMapped<T> implements ILogicPageList<JsonItem>
{
private final Container container;
public UsercontentLogicPageList(UsercontentAPI api, ActionHandlerFactory actionHandlerFactory, Content content, Container container, JsonWidget<T> widget)
{
super(api, actionHandlerFactory, content, widget);
this.container = container;
}
@Override
public void onClick(JsonItem item)
{
super.onClick(item);
this.container.initButtons();
}
@Override
public GuiButtonBase onRegister(int x, int y, int width, int height, String text, JsonItem item, ActionHandler actionHandler)
{
return new GuiButtonTooltip(x, y, width, height, text, this.toTooltip(item), actionHandler);
}
@Override
public void onInit(JsonItem item)
{
}
}
}

View File

@@ -0,0 +1,96 @@
package exopandora.worldhandler.usercontent.factory;
import exopandora.worldhandler.WorldHandler;
import exopandora.worldhandler.gui.content.Content;
import exopandora.worldhandler.gui.logic.ILogicMapped;
import exopandora.worldhandler.usercontent.UsercontentAPI;
import exopandora.worldhandler.usercontent.model.JsonItem;
import exopandora.worldhandler.usercontent.model.JsonWidget;
import exopandora.worldhandler.util.ActionHandler;
import exopandora.worldhandler.util.TextFormatting;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
@OnlyIn(Dist.CLIENT)
public abstract class WidgetFactory
{
private final ActionHandlerFactory actionHandlerFactory;
private final UsercontentAPI api;
public WidgetFactory(UsercontentAPI api, ActionHandlerFactory actionHandlerFactory)
{
this.api = api;
this.actionHandlerFactory = actionHandlerFactory;
}
public ActionHandlerFactory getActionHandlerFactory()
{
return this.actionHandlerFactory;
}
public UsercontentAPI getApi()
{
return this.api;
}
@OnlyIn(Dist.CLIENT)
public static class UsercontentLogicMapped<T extends Enum<T>> implements ILogicMapped<JsonItem>
{
private final ActionHandlerFactory actionHandlerFactory;
private final UsercontentAPI api;
private final Content content;
private final JsonWidget<T> widget;
public UsercontentLogicMapped(UsercontentAPI api, ActionHandlerFactory actionHandlerFactory, Content content, JsonWidget<T> widget)
{
this.api = api;
this.actionHandlerFactory = actionHandlerFactory;
this.content = content;
this.widget = widget;
}
@Override
public String translate(JsonItem item)
{
String translation = TextFormatting.formatNullable(item.getTranslation());
return translation == null ? item.getId() : translation;
}
@Override
public String toTooltip(JsonItem item)
{
return item.getId();
}
@Override
public void onClick(JsonItem item)
{
try
{
this.api.updateValue(this.widget.getAttributes().getId(), item.getId());
ActionHandler action = this.actionHandlerFactory.createActionHandler(this.content, this.widget.getAction(), item.getId());
if(action != null)
{
action.run();
}
}
catch(Exception e)
{
WorldHandler.LOGGER.error("Error executing action for widget");
}
}
@Override
public String getId()
{
return this.widget.getAttributes().getId();
}
@Override
public void onInit(JsonItem item)
{
this.onClick(item);
}
}
}

View File

@@ -0,0 +1,98 @@
package exopandora.worldhandler.usercontent.model;
import com.google.gson.annotations.SerializedName;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
@OnlyIn(Dist.CLIENT)
public class Action
{
@SerializedName("type")
private Type type;
@SerializedName("attributes")
private ActionAttributes attributes;
public Action(Type type, ActionAttributes attributes)
{
this.type = type;
this.attributes = attributes;
}
public Type getType()
{
return this.type;
}
public void setType(Type type)
{
this.type = type;
}
public ActionAttributes getAttributes()
{
return this.attributes;
}
public void setAttributes(ActionAttributes attributes)
{
this.attributes = attributes;
}
public void validate() throws IllegalStateException
{
if(this.type == null)
{
throw new IllegalStateException("action.type type is null");
}
if(this.type == Type.OPEN)
{
if(this.getAttributes() == null)
{
throw new IllegalStateException("action.attributes is null");
}
else if(this.getAttributes().getValue() == null)
{
throw new IllegalStateException("action.attributes.value is null");
}
}
else if(this.type == Type.SET)
{
if(this.getAttributes() == null)
{
throw new IllegalStateException("action.attributes is null");
}
}
else if(this.type == Type.RUN)
{
if(this.getAttributes() == null)
{
throw new IllegalStateException("action.attributes is null");
}
}
else if(this.type == Type.JS)
{
if(this.getAttributes() == null)
{
throw new IllegalStateException("action.attributes is null");
}
else if(this.getAttributes().getFunction() == null)
{
throw new IllegalStateException("action.attributes.function is null");
}
}
}
@OnlyIn(Dist.CLIENT)
public static enum Type
{
OPEN,
SET,
RUN,
BACK,
BACK_TO_GAME,
JS;
}
}

View File

@@ -0,0 +1,70 @@
package exopandora.worldhandler.usercontent.model;
import com.google.gson.annotations.SerializedName;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
@OnlyIn(Dist.CLIENT)
public class ActionAttributes
{
@SerializedName("function")
private String function;
@SerializedName("command")
private int command;
@SerializedName("index")
private int index;
@SerializedName("value")
private String value;
public ActionAttributes(String function, int command, int index, String value)
{
this.function = function;
this.command = command;
this.index = index;
this.value = value;
}
public String getFunction()
{
return this.function;
}
public void setFunction(String function)
{
this.function = function;
}
public int getCommand()
{
return this.command;
}
public void setCommand(int command)
{
this.command = command;
}
public int getIndex()
{
return this.index;
}
public void setIndex(int index)
{
this.index = index;
}
public String getValue()
{
return this.value;
}
public void setValue(String value)
{
this.value = value;
}
}

View File

@@ -0,0 +1,171 @@
package exopandora.worldhandler.usercontent.model;
import java.util.List;
import com.google.gson.annotations.SerializedName;
import exopandora.worldhandler.gui.button.EnumIcon;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
@OnlyIn(Dist.CLIENT)
public class Attributes
{
@SerializedName("id")
private String id;
@SerializedName("visible")
private BooleanExpression visible;
@SerializedName("enabled")
private BooleanExpression enabled;
@SerializedName("tooltip")
private String tooltip;
@SerializedName("item")
private String item;
@SerializedName("icon")
private EnumIcon icon;
@SerializedName("items")
private List<JsonItem> items = null;
@SerializedName("length")
private int length;
@SerializedName("min")
private double min;
@SerializedName("max")
private double max;
@SerializedName("start")
private double start;
public Attributes(String id, BooleanExpression visible, BooleanExpression enabled, String tooltip, String item, EnumIcon icon, List<JsonItem> items, int length, double min, double max, double start)
{
this.id = id;
this.visible = visible;
this.enabled = enabled;
this.tooltip = tooltip;
this.item = item;
this.icon = icon;
this.items = items;
this.length = length;
this.min = min;
this.max = max;
this.start = start;
}
public String getId()
{
return this.id;
}
public void setId(String id)
{
this.id = id;
}
public BooleanExpression getVisible()
{
return this.visible;
}
public void setVisible(BooleanExpression visible)
{
this.visible = visible;
}
public BooleanExpression getEnabled()
{
return this.enabled;
}
public void setEnabled(BooleanExpression enabled)
{
this.enabled = enabled;
}
public String getTooltip()
{
return this.tooltip;
}
public void setTooltip(String tooltip)
{
this.tooltip = tooltip;
}
public String getItem()
{
return this.item;
}
public void setItem(String item)
{
this.item = item;
}
public EnumIcon getIcon()
{
return this.icon;
}
public void setIcon(EnumIcon icon)
{
this.icon = icon;
}
public List<JsonItem> getItems()
{
return this.items;
}
public void setItems(List<JsonItem> items)
{
this.items = items;
}
public int getLength()
{
return this.length;
}
public void setLength(int length)
{
this.length = length;
}
public double getMin()
{
return this.min;
}
public void setMin(double min)
{
this.min = min;
}
public double getMax()
{
return this.max;
}
public void setMax(double max)
{
this.max = max;
}
public double getStart()
{
return this.start;
}
public void setStart(double start)
{
this.start = start;
}
}

View File

@@ -0,0 +1,78 @@
package exopandora.worldhandler.usercontent.model;
import com.google.gson.annotations.SerializedName;
import exopandora.worldhandler.usercontent.ScriptEngineAdapter;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
@OnlyIn(Dist.CLIENT)
public class BooleanExpression
{
@SerializedName("type")
private Type type;
@SerializedName("bool")
private boolean bool;
@SerializedName("function")
private String function;
public BooleanExpression(Type type, boolean bool, String function)
{
this.type = type;
this.bool = bool;
this.function = function;
}
public Type getType()
{
return this.type;
}
public void setType(Type type)
{
this.type = type;
}
public boolean isBool()
{
return this.bool;
}
public void setBool(boolean bool)
{
this.bool = bool;
}
public String getFunction()
{
return this.function;
}
public void setFunction(String function)
{
this.function = function;
}
public boolean eval(ScriptEngineAdapter engine)
{
if(this.type == Type.BOOL)
{
return this.bool;
}
else if(this.type == Type.JS)
{
return (boolean) engine.invokeFunction(this.function);
}
return true;
}
@OnlyIn(Dist.CLIENT)
public static enum Type
{
BOOL,
JS;
}
}

View File

@@ -0,0 +1,134 @@
package exopandora.worldhandler.usercontent.model;
import com.google.gson.annotations.SerializedName;
import exopandora.worldhandler.usercontent.model.JsonButton.Type;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
@OnlyIn(Dist.CLIENT)
public class JsonButton extends JsonWidget<Type>
{
@SerializedName("text")
private String text;
@SerializedName("type")
private Type type;
public JsonButton(String text, Type type, Action action, JsonDimensions dimensions, Attributes attributes)
{
super(action, dimensions, attributes);
this.text = text;
this.type = type;
}
public String getText()
{
return this.text;
}
public void setText(String text)
{
this.text = text;
}
public Type getType()
{
return this.type;
}
public void setType(Type type)
{
this.type = type;
}
@Override
public void validate() throws IllegalStateException
{
if(this.type == null)
{
throw new IllegalStateException("button.type is null");
}
if(this.type == Type.TEXTFIELD)
{
if(this.getAttributes() == null)
{
throw new IllegalStateException("button.attributes is null");
}
else if(this.getAttributes().getId() == null)
{
throw new IllegalStateException("button.attributes.id is null");
}
else if(this.getAttributes().getId().isEmpty())
{
throw new IllegalStateException("button.attributes.id is empty");
}
this.validateAction(Action.Type.SET, Action.Type.JS);
}
else if(this.type == Type.ITEM_BUTTON)
{
if(this.getAttributes() == null)
{
throw new IllegalStateException("button.attributes is null");
}
else if(this.getAttributes().getItem() == null)
{
throw new IllegalStateException("button.attributes.item is null");
}
this.validateAction(Action.Type.OPEN, Action.Type.SET, Action.Type.RUN, Action.Type.BACK, Action.Type.BACK_TO_GAME, Action.Type.JS);
}
else if(this.type == Type.ICON_BUTTON)
{
if(this.getAttributes() == null)
{
throw new IllegalStateException("button.attributes is null");
}
else if(this.getAttributes().getIcon() == null)
{
throw new IllegalStateException("button.attributes.icon is null");
}
this.validateAction(Action.Type.OPEN, Action.Type.SET, Action.Type.RUN, Action.Type.BACK, Action.Type.BACK_TO_GAME, Action.Type.JS);
}
else if(this.type == Type.LIST_BUTTON)
{
if(this.getAttributes() == null)
{
throw new IllegalStateException("button.attributes is null");
}
else if(this.getAttributes().getItems() == null)
{
throw new IllegalStateException("button.attributes.items is null");
}
else if(this.getAttributes().getItems().isEmpty())
{
throw new IllegalStateException("button.attributes.items is empty");
}
this.validateAction(Action.Type.SET, Action.Type.JS);
}
else if(this.type == Type.SLIDER)
{
if(this.getAttributes() == null)
{
throw new IllegalStateException("button.attributes is null");
}
this.validateAction(Action.Type.SET, Action.Type.JS);
}
}
@OnlyIn(Dist.CLIENT)
public static enum Type
{
BUTTON,
TEXTFIELD,
ITEM_BUTTON,
ICON_BUTTON,
LIST_BUTTON,
SLIDER;
}
}

View File

@@ -0,0 +1,59 @@
package exopandora.worldhandler.usercontent.model;
import java.util.List;
import com.google.gson.annotations.SerializedName;
import exopandora.worldhandler.builder.CommandSyntax.Argument;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
@OnlyIn(Dist.CLIENT)
public class JsonCommand
{
@SerializedName("name")
private String name;
@SerializedName("syntax")
private List<Argument> syntax = null;
@SerializedName("visible")
private BooleanExpression visible;
public JsonCommand(String name, List<Argument> syntax, BooleanExpression visible)
{
this.name = name;
this.syntax = syntax;
this.visible = visible;
}
public String getName()
{
return this.name;
}
public void setName(String name)
{
this.name = name;
}
public List<Argument> getSyntax()
{
return this.syntax;
}
public void setSyntax(List<Argument> syntax)
{
this.syntax = syntax;
}
public BooleanExpression getVisible()
{
return this.visible;
}
public void setVisible(BooleanExpression visible)
{
this.visible = visible;
}
}

View File

@@ -0,0 +1,70 @@
package exopandora.worldhandler.usercontent.model;
import com.google.gson.annotations.SerializedName;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
@OnlyIn(Dist.CLIENT)
public class JsonDimensions
{
@SerializedName("x")
private int x;
@SerializedName("y")
private int y;
@SerializedName("width")
private int width;
@SerializedName("height")
private int height;
public JsonDimensions(int x, int y, int width, int height)
{
this.x = x;
this.y = y;
this.width = width;
this.height = height;
}
public int getX()
{
return this.x;
}
public void setX(int x)
{
this.x = x;
}
public int getY()
{
return this.y;
}
public void setY(int y)
{
this.y = y;
}
public int getWidth()
{
return this.width;
}
public void setWidth(int width)
{
this.width = width;
}
public int getHeight()
{
return this.height;
}
public void setHeight(int height)
{
this.height = height;
}
}

View File

@@ -0,0 +1,71 @@
package exopandora.worldhandler.usercontent.model;
import com.google.gson.annotations.SerializedName;
import exopandora.worldhandler.usercontent.model.JsonElement.Type;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
@OnlyIn(Dist.CLIENT)
public class JsonElement extends JsonWidget<Type>
{
@SerializedName("type")
private Type type;
public JsonElement(Type type, Action action, JsonDimensions dimensions, Attributes attributes)
{
super(action, dimensions, attributes);
this.type = type;
}
public Type getType()
{
return this.type;
}
public void setType(Type type)
{
this.type = type;
}
@OnlyIn(Dist.CLIENT)
public static enum Type
{
PAGE_LIST;
}
@Override
public void validate() throws IllegalStateException
{
if(this.type == null)
{
throw new IllegalStateException("element.type is null");
}
if(this.type == Type.PAGE_LIST)
{
if(this.getAttributes() == null)
{
throw new IllegalStateException("element.attributes is null");
}
else if(this.getAttributes().getId() == null)
{
throw new IllegalStateException("element.attributes.id is null");
}
else if(this.getAttributes().getId().isEmpty())
{
throw new IllegalStateException("element.attributes.id is empty");
}
else if(this.getAttributes().getItems() == null)
{
throw new IllegalStateException("element.attributes.items is null");
}
else if(this.getAttributes().getItems().isEmpty())
{
throw new IllegalStateException("element.attributes.items is empty");
}
this.validateAction(Action.Type.SET, Action.Type.JS);
}
}
}

View File

@@ -0,0 +1,96 @@
package exopandora.worldhandler.usercontent.model;
import java.util.List;
import com.google.gson.annotations.SerializedName;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
@OnlyIn(Dist.CLIENT)
public class JsonGui
{
@SerializedName("title")
private String title;
@SerializedName("tab")
private JsonTab tab;
@SerializedName("buttons")
private List<JsonButton> buttons = null;
@SerializedName("elements")
private List<JsonElement> elements = null;
@SerializedName("texts")
private List<JsonText> texts = null;
public JsonGui(String title, JsonTab tab, List<JsonButton> buttons, List<JsonElement> elements, List<JsonText> texts)
{
this.title = title;
this.tab = tab;
this.buttons = buttons;
this.elements = elements;
this.texts = texts;
}
public String getTitle()
{
return this.title;
}
public void setTitle(String title)
{
this.title = title;
}
public JsonTab getTab()
{
return this.tab;
}
public void setTitle(JsonTab tab)
{
this.tab = tab;
}
public List<JsonButton> getButtons()
{
return this.buttons;
}
public void setButtons(List<JsonButton> buttons)
{
this.buttons = buttons;
}
public List<JsonElement> getElements()
{
return this.elements;
}
public void setElements(List<JsonElement> elements)
{
this.elements = elements;
}
public List<JsonText> getTexts()
{
return this.texts;
}
public void setTexts(List<JsonText> texts)
{
this.texts = texts;
}
public void validate() throws IllegalStateException
{
if(this.tab == null)
{
throw new IllegalStateException("gui.tab is null");
}
this.tab.validate();
}
}

View File

@@ -0,0 +1,42 @@
package exopandora.worldhandler.usercontent.model;
import com.google.gson.annotations.SerializedName;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
@OnlyIn(Dist.CLIENT)
public class JsonItem
{
@SerializedName("id")
private String id;
@SerializedName("translation")
private String translation;
public JsonItem(String id, String translation)
{
this.id = id;
this.translation = translation;
}
public String getId()
{
return this.id;
}
public void setId(String id)
{
this.id = id;
}
public String getTranslation()
{
return this.translation;
}
public void setTranslation(String translation)
{
this.translation = translation;
}
}

View File

@@ -0,0 +1,30 @@
package exopandora.worldhandler.usercontent.model;
import java.util.List;
import com.google.gson.annotations.SerializedName;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
@OnlyIn(Dist.CLIENT)
public class JsonModel
{
@SerializedName("commands")
private List<JsonCommand> commands = null;
public JsonModel(List<JsonCommand> commands)
{
this.commands = commands;
}
public List<JsonCommand> getCommands()
{
return this.commands;
}
public void setCommands(List<JsonCommand> commands)
{
this.commands = commands;
}
}

View File

@@ -0,0 +1,78 @@
package exopandora.worldhandler.usercontent.model;
import com.google.gson.annotations.SerializedName;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
@OnlyIn(Dist.CLIENT)
public class JsonTab
{
@SerializedName("title")
private String title;
@SerializedName("category")
private String category;
@SerializedName("category_index")
private int categoryIndex;
@SerializedName("active_content")
private String activeContent;
public JsonTab(String title, String category, int categoryIndex, String activeContent)
{
this.title = title;
this.category = category;
this.categoryIndex = categoryIndex;
this.activeContent = activeContent;
}
public String getTitle()
{
return this.title;
}
public void setTitle(String title)
{
this.title = title;
}
public String getCategory()
{
return this.category;
}
public void setCategory(String category)
{
this.category = category;
}
public int getCategoryIndex()
{
return this.categoryIndex;
}
public void setCategoryIndex(int categoryIndex)
{
this.categoryIndex = categoryIndex;
}
public String getActiveContent()
{
return this.activeContent;
}
public void setActiveContent(String activeContent)
{
this.activeContent = activeContent;
}
public void validate() throws IllegalStateException
{
if(this.category == null)
{
throw new IllegalStateException("tab.category is null");
}
}
}

View File

@@ -0,0 +1,70 @@
package exopandora.worldhandler.usercontent.model;
import com.google.gson.annotations.SerializedName;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
@OnlyIn(Dist.CLIENT)
public class JsonText
{
@SerializedName("text")
private String text;
@SerializedName("x")
private int x;
@SerializedName("y")
private int y;
@SerializedName("color")
private int color;
public JsonText(String text, int x, int y, int color)
{
this.text = text;
this.x = x;
this.y = y;
this.color = color;
}
public String getText()
{
return this.text;
}
public void setText(String text)
{
this.text = text;
}
public int getX()
{
return this.x;
}
public void setX(int x)
{
this.x = x;
}
public int getY()
{
return this.y;
}
public void setY(int y)
{
this.y = y;
}
public int getColor()
{
return this.color;
}
public void setColor(int color)
{
this.color = color;
}
}

View File

@@ -0,0 +1,52 @@
package exopandora.worldhandler.usercontent.model;
import com.google.gson.annotations.SerializedName;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
@OnlyIn(Dist.CLIENT)
public class JsonUsercontent
{
@SerializedName("model")
private JsonModel model;
@SerializedName("gui")
private JsonGui gui;
public JsonUsercontent(JsonModel model, JsonGui gui)
{
this.model = model;
this.gui = gui;
}
public JsonModel getModel()
{
return this.model;
}
public void setModel(JsonModel model)
{
this.model = model;
}
public JsonGui getGui()
{
return this.gui;
}
public void setGui(JsonGui gui)
{
this.gui = gui;
}
public void validate() throws IllegalStateException
{
if(this.gui == null)
{
throw new IllegalStateException("gui is null");
}
this.gui.validate();
}
}

View File

@@ -0,0 +1,82 @@
package exopandora.worldhandler.usercontent.model;
import java.util.Arrays;
import com.google.gson.annotations.SerializedName;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
@OnlyIn(Dist.CLIENT)
public abstract class JsonWidget<T extends Enum<T>>
{
@SerializedName("action")
private Action action;
@SerializedName("dimensions")
private JsonDimensions dimensions;
@SerializedName("attributes")
private Attributes attributes;
public JsonWidget(Action action, JsonDimensions dimensions, Attributes attributes)
{
this.action = action;
this.dimensions = dimensions;
this.attributes = attributes;
}
public Action getAction()
{
return this.action;
}
public void setAction(Action action)
{
this.action = action;
}
public JsonDimensions getDimensions()
{
return this.dimensions;
}
public void setDimensions(JsonDimensions dimensions)
{
this.dimensions = dimensions;
}
public Attributes getAttributes()
{
return this.attributes;
}
public void setAttributes(Attributes attributes)
{
this.attributes = attributes;
}
public abstract T getType();
public abstract void setType(T type);
public abstract void validate() throws IllegalStateException;
protected void validateAction(Action.Type... allowedTypes) throws IllegalStateException
{
if(this.getAction() != null)
{
this.getAction().validate();
if(Arrays.stream(allowedTypes).noneMatch(type -> type.equals(this.getAction().getType())))
{
throw new IllegalStateException("Illegal action for type " + this.getType().toString().toLowerCase());
}
}
}
@OnlyIn(Dist.CLIENT)
public static enum Type
{
BUTTON,
ELEMENT
}
}