Panel template (#1841)

* Implement basic functionality to read data from template panels.

Create TemplateReader class that has static method which generates a PanelTemplateRecord. This record contains every necessary information from user created template file so everyone could use it to generate a functional panel.
These classes are just for reading templates and do not create actual panel.

* Add template clearing via bentobox reload command.
This commit is contained in:
BONNe 2021-08-30 00:34:05 +03:00 committed by GitHub
parent faf351fd59
commit 2607256c06
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 488 additions and 0 deletions

View File

@ -0,0 +1,87 @@
//
// Created by BONNe
// Copyright - 2021
//
package world.bentobox.bentobox.api.panels.reader;
import org.bukkit.event.inventory.ClickType;
import org.bukkit.inventory.ItemStack;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* This Record contains all necessary information about Item Template that can be used to craft panel item.
*
* @param icon ItemStack of the Item
* @param title Title of the item
* @param description Lore message of the item
* @param actions List of Actions for a button
* @param dataMap DataMap that links additional objects for a button.
* @param fallback FallBack item if current one is not possible to generate.
*
* @since 1.17.3
*/
public record ItemTemplateRecord(ItemStack icon,
String title,
String description,
List<ActionRecords> actions,
Map<String, Object> dataMap,
ItemTemplateRecord fallback)
{
/**
* Instantiates a new Item template record without actions and data map.
*
* @param icon the icon
* @param title the title
* @param description the description
* @param fallback the fallback
*/
public ItemTemplateRecord(ItemStack icon, String title, String description, ItemTemplateRecord fallback)
{
this(icon, title, description, new ArrayList<>(6), new HashMap<>(0), fallback);
}
/**
* This method adds given object associated with key into data map.
* @param key Key value of object.
* @param data Data that is associated with a key.
*/
public void addData(String key, Object data)
{
this.dataMap.put(key, data);
}
/**
* Add action to the actions list.
*
* @param actionData the action data
*/
public void addAction(ActionRecords actionData)
{
this.actions.add(actionData);
}
// ---------------------------------------------------------------------
// Section: Classes
// ---------------------------------------------------------------------
/**
* The Action Records holds data about each action.
*
* @param clickType the click type
* @param actionType the string that represents action type
* @param content the content of the action
* @param tooltip the tooltip of action
*/
public record ActionRecords(ClickType clickType, String actionType, String content, String tooltip) {}
}

View File

@ -0,0 +1,74 @@
//
// Created by BONNe
// Copyright - 2021
//
package world.bentobox.bentobox.api.panels.reader;
import org.bukkit.inventory.ItemStack;
import world.bentobox.bentobox.api.panels.Panel;
/**
* This is template object for the panel reader. It contains data that can exist in the panel.
* PanelBuilder will use this to build panel.
*
* @param type the type of GUI
* @param title the title of GUI
* @param border the border block for GUI
* @param background the background block for GUI.
* @param content The 2D array of ItemTemplateRecords
*
* @since 1.17.3
*/
public record PanelTemplateRecord(Panel.Type type,
String title,
TemplateItem border,
TemplateItem background,
ItemTemplateRecord[][] content)
{
/**
* Instantiates a new Panel template record with empty content.
*
* @param type the type
* @param title the title
* @param border the border
* @param background the background
*/
public PanelTemplateRecord(Panel.Type type, String title, TemplateItem border, TemplateItem background)
{
this(type, title, border, background, new ItemTemplateRecord[6][9]);
}
/**
* This method adds give item template record in given slot.
* @param rowIndex row index of content array
* @param columnIndex column index of content array.
* @param panelItemTemplate item template record that must be added.
*/
public void addButtonTemplate(int rowIndex, int columnIndex, ItemTemplateRecord panelItemTemplate)
{
this.content[rowIndex][columnIndex] = panelItemTemplate;
}
// ---------------------------------------------------------------------
// Section: Classes
// ---------------------------------------------------------------------
/**
* This record contains info about border and background item.
*/
public record TemplateItem(ItemStack icon, String title, String description)
{
public TemplateItem(ItemStack icon)
{
this(icon, null, null);
}
}
}

View File

@ -0,0 +1,324 @@
//
// Created by BONNe
// Copyright - 2021
//
package world.bentobox.bentobox.api.panels.reader;
import com.google.common.base.Enums;
import org.bukkit.Material;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.configuration.InvalidConfigurationException;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.event.inventory.ClickType;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import world.bentobox.bentobox.api.panels.Panel;
import world.bentobox.bentobox.util.ItemParser;
/**
* This class manages Template file reading, creating PanelTemplateRecord object and storing it internally.
* This class just reads and returns given panel template. It does not create a functional panel.
*
* @since 1.17.3
*/
public class TemplateReader
{
/**
* Read template panel panel template record.
*
* @param panelName the panel name
* @param panelLocation the panel location directory
* @return the panel template record
*/
public static PanelTemplateRecord readTemplatePanel(@NonNull String panelName, @NonNull File panelLocation)
{
if (!panelLocation.exists())
{
// Return null because folder does not exist.
return null;
}
File file = new File(panelLocation, panelName.endsWith(".yml") ? panelName : panelName + ".yml");
if (!file.exists())
{
// Return as file does not exist.
return null;
}
// Check if panel is already crafted.
if (TemplateReader.loadedPanels.containsKey(file.getAbsolutePath()))
{
return TemplateReader.loadedPanels.get(file.getAbsolutePath());
}
PanelTemplateRecord record;
try
{
// Load config
YamlConfiguration config = new YamlConfiguration();
config.load(file);
// Read panel
record = readPanelTemplate(config.getConfigurationSection(panelName));
// Put panel into memory
TemplateReader.loadedPanels.put(file.getAbsolutePath(), record);
}
catch (IOException | InvalidConfigurationException e)
{
record = null;
}
return record;
}
/**
* This method reads panel template from given configuration section.
* @param configurationSection Section that contains panel template data.
* @return Panel Template.
*/
private static PanelTemplateRecord readPanelTemplate(@Nullable ConfigurationSection configurationSection)
{
if (configurationSection == null)
{
// No data to return.
return null;
}
String title = configurationSection.getString("title");
Panel.Type type = configurationSection.getObject("type", Panel.Type.class);
PanelTemplateRecord.TemplateItem borderItem = null;
// Read Border Icon.
if (configurationSection.isConfigurationSection("border"))
{
// Process border icon if it contains more options.
ConfigurationSection borderSection = configurationSection.getConfigurationSection("border");
if (borderSection != null)
{
borderItem = new PanelTemplateRecord.TemplateItem(
ItemParser.parse((borderSection.getString("icon", Material.AIR.name()))),
borderSection.getString("name", null),
borderSection.getString("description", null));
}
}
else if (configurationSection.isString("border"))
{
// Process border icon if it contains only icon.
borderItem = new PanelTemplateRecord.TemplateItem(
ItemParser.parse((configurationSection.getString("border", Material.AIR.name()))));
}
PanelTemplateRecord.TemplateItem backgroundItem = null;
// Read Background block
if (configurationSection.isConfigurationSection("background"))
{
// Process border icon if it contains more options.
ConfigurationSection backgroundSection = configurationSection.getConfigurationSection("background");
if (backgroundSection != null)
{
backgroundItem = new PanelTemplateRecord.TemplateItem(
ItemParser.parse((backgroundSection.getString("icon", Material.AIR.name()))),
backgroundSection.getString("name", null),
backgroundSection.getString("description", null));
}
}
else if (configurationSection.isString("background"))
{
// Process background icon if it contains only icon.
backgroundItem = new PanelTemplateRecord.TemplateItem(
ItemParser.parse((configurationSection.getString("background", Material.AIR.name()))));
}
// Read reusable
Map<String, ItemTemplateRecord> panelItemDataMap = new HashMap<>();
ConfigurationSection reusable = configurationSection.getConfigurationSection("reusable");
if (reusable != null)
{
// Add all reusables to the local storage.
reusable.getKeys(false).forEach(key ->
readPanelItemTemplate(reusable.getConfigurationSection(key), key, panelItemDataMap));
}
// Create template record.
PanelTemplateRecord template = new PanelTemplateRecord(type, title, borderItem, backgroundItem);
// Read content
ConfigurationSection content = configurationSection.getConfigurationSection("content");
if (content == null)
{
// Return empty template.
return template;
}
for (int rowIndex = 0; rowIndex < 6; rowIndex++)
{
// Read each line.
if (content.isConfigurationSection(String.valueOf(rowIndex + 1)))
{
ConfigurationSection line = content.getConfigurationSection(String.valueOf(rowIndex + 1));
if (line != null)
{
// Populate existing lines with items.
for (int columnIndex = 0; columnIndex < 9; columnIndex++)
{
if (line.isConfigurationSection(String.valueOf(columnIndex + 1)))
{
// If it contains a section, then build a new button template from it.
template.addButtonTemplate(rowIndex,
columnIndex,
readPanelItemTemplate(line.getConfigurationSection(String.valueOf(columnIndex + 1))));
}
else if (line.isString(String.valueOf(columnIndex + 1)))
{
// If it contains just a single word, assume it is a reusable.
template.addButtonTemplate(rowIndex,
columnIndex,
panelItemDataMap.get(line.getString(String.valueOf(columnIndex + 1))));
}
}
}
}
}
// Garbage collector.
panelItemDataMap.clear();
return template;
}
/**
* This method creates PanelItemTemplate from a given configuration section.
* @param section Section that should contain all information about the panel item template.
* @return PanelItemTemplate that should represent button from a section.
*/
@Nullable
private static ItemTemplateRecord readPanelItemTemplate(@Nullable ConfigurationSection section)
{
return readPanelItemTemplate(section, null, null);
}
/**
* This method creates PanelItemTemplate from a given configuration section.
* @param section Section that should contain all information about the panel item template.
* @return PanelItemTemplate that should represent button from a section.
*/
@Nullable
private static ItemTemplateRecord readPanelItemTemplate(@Nullable ConfigurationSection section,
String itemKey,
Map<String, ItemTemplateRecord> reusableItemMap)
{
if (section == null)
{
// No section, no item.
return null;
}
ItemTemplateRecord fallback;
if (section.isConfigurationSection("fallback"))
{
fallback = readPanelItemTemplate(section.getConfigurationSection("fallback"));
}
else if (section.isString("fallback") && reusableItemMap != null)
{
fallback = reusableItemMap.get(section.getString("fallback"));
}
else
{
fallback = null;
}
// Create Item Record
ItemTemplateRecord itemRecord = new ItemTemplateRecord(ItemParser.parse(section.getString("icon")),
section.getString("title", null),
section.getString("description", null),
fallback);
// Read data
if (section.isConfigurationSection("data"))
{
ConfigurationSection dataSection = section.getConfigurationSection("data");
if (dataSection != null)
{
dataSection.getKeys(false).forEach(key -> itemRecord.addData(key, dataSection.get(key)));
}
}
// Read Click data
if (section.isConfigurationSection("actions"))
{
ConfigurationSection actionSection = section.getConfigurationSection("actions");
if (actionSection != null)
{
actionSection.getKeys(false).forEach(actionKey -> {
ClickType clickType = Enums.getIfPresent(ClickType.class, actionKey.toUpperCase()).orNull();
if (clickType != null)
{
ConfigurationSection actionDataSection = actionSection.getConfigurationSection(actionKey);
if (actionDataSection != null)
{
ItemTemplateRecord.ActionRecords actionData =
new ItemTemplateRecord.ActionRecords(clickType,
actionDataSection.getString("type"),
actionDataSection.getString("content"),
actionDataSection.getString("tooltip"));
itemRecord.addAction(actionData);
}
}
});
}
}
// Add item to the map
if (reusableItemMap != null && itemKey != null)
{
reusableItemMap.put(itemKey, itemRecord);
}
return itemRecord;
}
/**
* This method clears loaded panels from the cache.
*/
public static void clearPanels()
{
loadedPanels.clear();
}
/**
* This map contains already read panels and their location.
* This improves performance for GUI opening, with a some memory usage.
*/
private static final Map<String, PanelTemplateRecord> loadedPanels = new HashMap<>();
}

View File

@ -7,6 +7,7 @@ import org.bukkit.Bukkit;
import world.bentobox.bentobox.api.commands.CompositeCommand;
import world.bentobox.bentobox.api.commands.ConfirmableCommand;
import world.bentobox.bentobox.api.events.BentoBoxReadyEvent;
import world.bentobox.bentobox.api.panels.reader.TemplateReader;
import world.bentobox.bentobox.api.user.User;
import world.bentobox.bentobox.commands.reload.BentoBoxReloadLocalesCommand;
import world.bentobox.bentobox.listeners.PanelListenerManager;
@ -45,6 +46,8 @@ public class BentoBoxReloadCommand extends ConfirmableCommand {
// Close all open panels
PanelListenerManager.closeAllPanels();
// Clear all template panels.
TemplateReader.clearPanels();
// Reload settings
getPlugin().loadSettings();