├── META-INF └── MANIFEST.MF ├── src └── main │ └── java │ └── com │ └── codehusky │ └── huskyui │ ├── states │ ├── element │ │ ├── package-info.java │ │ ├── Element.java │ │ └── ActionableElement.java │ ├── action │ │ ├── runnable │ │ │ ├── package-info.java │ │ │ ├── UIRunnable.java │ │ │ └── RunnableAction.java │ │ ├── package-info.java │ │ ├── ActionType.java │ │ ├── CommandAction.java │ │ └── Action.java │ ├── package-info.java │ ├── State.java │ └── Page.java │ ├── package-info.java │ ├── InventoryUtil.java │ ├── StateContainer.java │ ├── ElementRegistry.java │ └── HuskyUI.java ├── .gitignore ├── pom.xml ├── README.md └── LICENSE /META-INF/MANIFEST.MF: -------------------------------------------------------------------------------- 1 | Manifest-Version: 1.0 2 | 3 | -------------------------------------------------------------------------------- /src/main/java/com/codehusky/huskyui/states/element/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Contains the types of {@link com.codehusky.huskyui.states.element.Element}s 3 | * to be used in a {@link com.codehusky.huskyui.states.Page}. 4 | */ 5 | package com.codehusky.huskyui.states.element; -------------------------------------------------------------------------------- /src/main/java/com/codehusky/huskyui/states/action/runnable/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * When {@link com.codehusky.huskyui.states.action.Action}s just aren't enough, 3 | * and developers wanna perform neat actions when their GUIs are used. 4 | */ 5 | package com.codehusky.huskyui.states.action.runnable; -------------------------------------------------------------------------------- /src/main/java/com/codehusky/huskyui/states/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Contains the {@link com.codehusky.huskyui.states.State} and 3 | * {@link com.codehusky.huskyui.states.Page} classes, meant to 4 | * facilitate data to a player in the form of a chest-based GUI. 5 | */ 6 | package com.codehusky.huskyui.states; -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.class 2 | 3 | # Mobile Tools for Java (J2ME) 4 | .mtj.tmp/ 5 | 6 | # Package Files # 7 | *.jar 8 | *.war 9 | *.ear 10 | 11 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 12 | hs_err_pid* 13 | .idea/ 14 | *.iml 15 | target/ 16 | 17 | # gradle 18 | .gradle/ 19 | build/ -------------------------------------------------------------------------------- /src/main/java/com/codehusky/huskyui/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Main package for HuskyUI. 3 | * 4 | *

Contains the main {@link org.spongepowered.api.plugin.Plugin} 5 | * annotated class for loading by Sponge. Also contains the container 6 | * for {@link com.codehusky.huskyui.states.State}s.

7 | */ 8 | package com.codehusky.huskyui; -------------------------------------------------------------------------------- /src/main/java/com/codehusky/huskyui/states/action/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Actions, primarily, are the tool to move {@link org.spongepowered.api.entity.living.player.Player}s 3 | * throughout the GUI interface. Those actions are defined in this package. 4 | * 5 | *

Notice {@link com.codehusky.huskyui.states.action.runnable.RunnableAction} in the child 6 | * package of this one, where other, developer-defined actions can be determined based on 7 | * a Player's selection.

8 | */ 9 | package com.codehusky.huskyui.states.action; -------------------------------------------------------------------------------- /src/main/java/com/codehusky/huskyui/InventoryUtil.java: -------------------------------------------------------------------------------- 1 | package com.codehusky.huskyui; 2 | 3 | import org.spongepowered.api.entity.living.player.Player; 4 | import org.spongepowered.api.item.inventory.Inventory; 5 | import org.spongepowered.api.scheduler.Task; 6 | 7 | public class InventoryUtil { 8 | public static void close(Player player) { 9 | Task.builder().execute(()-> 10 | player.closeInventory() 11 | ).delayTicks(1).submit(HuskyUI.getInstance()); 12 | } 13 | 14 | public static void open(Player player, Inventory inventory) { 15 | Task.builder().execute(()-> { 16 | player.openInventory(inventory); 17 | }).delayTicks(1).submit(HuskyUI.getInstance()); 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/com/codehusky/huskyui/states/action/runnable/UIRunnable.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of HuskyUI. 3 | * 4 | * HuskyUI is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * HuskyUI is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with HuskyUI. If not, see . 16 | */ 17 | 18 | package com.codehusky.huskyui.states.action.runnable; 19 | 20 | import javax.annotation.Nonnull; 21 | 22 | /** 23 | * A pre-determined action to be run when interfacing with a Page. 24 | */ 25 | public interface UIRunnable { 26 | 27 | /** 28 | * The additional actions to be run when interfacing with a Page. 29 | * 30 | * @param context the RunnableAction that relates to this runnable 31 | */ 32 | void run(@Nonnull final RunnableAction context); 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/com/codehusky/huskyui/states/action/ActionType.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of HuskyUI. 3 | * 4 | * HuskyUI is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * HuskyUI is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with HuskyUI. If not, see . 16 | */ 17 | 18 | package com.codehusky.huskyui.states.action; 19 | 20 | /** 21 | * The type of {@link Action} to be performed. 22 | */ 23 | public enum ActionType { 24 | 25 | /** 26 | * Triggered if a {@link org.spongepowered.api.entity.living.player.Player} 27 | * is going "Back" to the parent. 28 | */ 29 | BACK, 30 | 31 | /** 32 | * Triggered if a {@link org.spongepowered.api.entity.living.player.Player} 33 | * is closing the {@link org.spongepowered.api.item.inventory.Inventory}. 34 | */ 35 | CLOSE, 36 | 37 | /** 38 | * Triggered for a normal action, usually forward. 39 | */ 40 | NORMAL, 41 | 42 | REFRESH, 43 | 44 | /** 45 | * This is a false flag operation. We've been swindled! 46 | * Quick, scramble the jets! 47 | */ 48 | NONE 49 | } 50 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | com.codehusky 8 | HuskyUI 9 | 0.6.0PRE3 10 | 11 | 12 | 13 | sponge 14 | http://repo.spongepowered.org/maven 15 | 16 | 17 | 18 | 19 | 20 | org.spongepowered 21 | spongeapi 22 | 7.0.0-SNAPSHOT 23 | 24 | provided 25 | 26 | 27 | org.apache.maven.plugins 28 | maven-javadoc-plugin 29 | 3.0.0 30 | maven-plugin 31 | 32 | 33 | 34 | 1.8 35 | 1.8 36 | 37 | 38 | 39 | 40 | 41 | org.apache.maven.plugins 42 | maven-javadoc-plugin 43 | 3.0.0 44 | 45 | 46 | https://jd.spongepowered.org/7.0.0/ 47 | 48 | 49 | **/Metrics.java 50 | 51 | 52 | 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /src/main/java/com/codehusky/huskyui/states/element/Element.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of HuskyUI. 3 | * 4 | * HuskyUI is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * HuskyUI is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with HuskyUI. If not, see . 16 | */ 17 | 18 | package com.codehusky.huskyui.states.element; 19 | 20 | import com.codehusky.huskyui.StateContainer; 21 | import org.spongepowered.api.item.inventory.ItemStack; 22 | import javax.annotation.Nonnull; 23 | 24 | /** 25 | * A basic Element is, essentially, a simple wrapper for 26 | * an {@link ItemStack} and serves no purpose. 27 | */ 28 | public class Element { 29 | 30 | /** 31 | * The {@link ItemStack} being wrapped. 32 | */ 33 | @Nonnull private final ItemStack item; 34 | 35 | /** 36 | * Constructs a new Element. 37 | * 38 | * @param item the {@link ItemStack} to be wrapped 39 | */ 40 | public Element(@Nonnull final ItemStack item) { 41 | this.item = item; 42 | } 43 | 44 | /** 45 | * Gets the {@link ItemStack} being wrapped. 46 | * 47 | * @return the wrapped ItemStack 48 | */ 49 | @Nonnull 50 | public ItemStack getItem() { 51 | return this.item; 52 | } 53 | 54 | /** 55 | * Creates a copy of this Element. 56 | * 57 | * @param newContainer the {@link StateContainer} now 58 | * responsible for this Element 59 | * @return a copy of this Element 60 | */ 61 | @Nonnull 62 | public Element copy(@Nonnull final StateContainer newContainer) { // We don't use this on purpose. 63 | return new Element(this.item.copy()); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/main/java/com/codehusky/huskyui/states/element/ActionableElement.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of HuskyUI. 3 | * 4 | * HuskyUI is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * HuskyUI is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with HuskyUI. If not, see . 16 | */ 17 | 18 | package com.codehusky.huskyui.states.element; 19 | 20 | import com.codehusky.huskyui.StateContainer; 21 | import com.codehusky.huskyui.states.action.Action; 22 | import com.codehusky.huskyui.states.action.CommandAction; 23 | import org.spongepowered.api.item.inventory.ItemStack; 24 | import javax.annotation.Nonnull; 25 | 26 | /** 27 | * An extension of {@link Element} that also wraps 28 | * an {@link Action} to be used, typically, by an event. 29 | */ 30 | public class ActionableElement extends Element { 31 | 32 | /** 33 | * The {@link Action} to be performed on 34 | * on this Element. 35 | */ 36 | @Nonnull private Action action; 37 | 38 | /** 39 | * Constructs a new ActionableElement. 40 | * 41 | * @param action the {@link Action} to be performed 42 | * @param item the {@link ItemStack} this {@link Element} wraps 43 | */ 44 | public ActionableElement(@Nonnull final Action action, @Nonnull final ItemStack item) { 45 | super(item); 46 | this.action = action; 47 | } 48 | 49 | /** 50 | * Gets the {@link Action} to be performed on this Element. 51 | * 52 | * @return the Action to be performed on this Element 53 | */ 54 | @Nonnull 55 | public Action getAction() { 56 | return this.action; 57 | } 58 | 59 | /** 60 | * Creates a copy of this ActionableElement. 61 | * 62 | * @param newContainer the {@link StateContainer} now 63 | * responsible for this ActionableElement 64 | * @return a copy of this ActionableElement 65 | */ 66 | @Nonnull 67 | @Override 68 | public ActionableElement copy(@Nonnull final StateContainer newContainer) { 69 | return new ActionableElement(this.action.copy(newContainer), this.getItem().copy()); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/main/java/com/codehusky/huskyui/states/action/CommandAction.java: -------------------------------------------------------------------------------- 1 | package com.codehusky.huskyui.states.action; 2 | 3 | import com.codehusky.huskyui.StateContainer; 4 | import com.codehusky.huskyui.states.State; 5 | import org.spongepowered.api.Sponge; 6 | import org.spongepowered.api.item.inventory.Inventory; 7 | 8 | import javax.annotation.Nonnull; 9 | /** 10 | * This class is used to handle situations requiring command execution by an item. 11 | * You should not use this class to open another UI state container, use a {@link com.codehusky.huskyui.states.action.runnable.RunnableAction} for that. 12 | */ 13 | public class CommandAction extends Action { 14 | @Nonnull private final String command; 15 | @Nonnull private CommandReceiver receiver; 16 | 17 | /** 18 | * Constructs a CommandAction. 19 | * 20 | * @param container the {@link StateContainer} that is responsible for this Action 21 | * @param type the type of Action taking place 22 | * @param goalState the intended {@link State} 23 | * @param command The command intended to run 24 | */ 25 | public CommandAction(@Nonnull StateContainer container, @Nonnull ActionType type, @Nonnull String goalState, @Nonnull final String command) { 26 | super(container, type, goalState); 27 | this.command = command; 28 | this.receiver = CommandReceiver.SERVER; 29 | } 30 | 31 | /** 32 | * Constructs a CommandAction. 33 | * 34 | * @param container the {@link StateContainer} that is responsible for this Action 35 | * @param type the type of Action taking place 36 | * @param goalState the intended {@link State} 37 | * @param command the command intended to run 38 | * @param receiver the {@link CommandReceiver} intended to run the command 39 | */ 40 | public CommandAction(@Nonnull StateContainer container, @Nonnull ActionType type, @Nonnull String goalState, 41 | @Nonnull final String command, @Nonnull CommandReceiver receiver) { 42 | super(container, type, goalState); 43 | this.command = command; 44 | this.receiver = receiver; 45 | } 46 | 47 | @Override 48 | public void runAction(@Nonnull String currentState, Inventory inventory) { 49 | Sponge.getCommandManager().process(this.receiver == CommandReceiver.SERVER ? 50 | Sponge.getServer().getConsole() : getObserver(), this.command); 51 | super.runAction(currentState,inventory); 52 | } 53 | 54 | @Nonnull 55 | public String getCommand() { 56 | return command; 57 | } 58 | 59 | @Nonnull 60 | public CommandReceiver getReceiver() { 61 | return receiver; 62 | } 63 | 64 | /** 65 | * Set the {@link CommandReceiver} of the CommandAction 66 | * 67 | * @param receiver the {@link CommandReceiver} that will replace the previously specified one. 68 | */ 69 | public void setReceiver(@Nonnull CommandReceiver receiver) { 70 | this.receiver = receiver; 71 | } 72 | 73 | public enum CommandReceiver { 74 | PLAYER, 75 | SERVER 76 | } 77 | 78 | /** 79 | * Creates a copy of this CommandAction. 80 | * 81 | * @param newContainer the new {@link StateContainer} to be responsible for this new Action 82 | * @return a copy of this Action 83 | */ 84 | @Nonnull 85 | public Action copy(@Nonnull final StateContainer newContainer) { 86 | return new CommandAction(newContainer, this.getType(), this.getGoalState(),command,receiver); 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/main/java/com/codehusky/huskyui/states/action/runnable/RunnableAction.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of HuskyUI. 3 | * 4 | * HuskyUI is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * HuskyUI is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with HuskyUI. If not, see . 16 | */ 17 | 18 | package com.codehusky.huskyui.states.action.runnable; 19 | 20 | import com.codehusky.huskyui.StateContainer; 21 | import com.codehusky.huskyui.states.action.Action; 22 | import com.codehusky.huskyui.states.action.ActionType; 23 | import org.spongepowered.api.item.inventory.Inventory; 24 | import org.spongepowered.api.text.Text; 25 | import org.spongepowered.api.text.format.TextColors; 26 | 27 | import javax.annotation.Nonnull; 28 | import javax.annotation.Nullable; 29 | 30 | /** 31 | * A separate type of {@link Action} which performs developer-defined 32 | * actions separate from navigating the GUI. 33 | */ 34 | public class RunnableAction extends Action { 35 | 36 | /** 37 | * The UIRunnable that determines this action to be taken. 38 | */ 39 | @Nullable private UIRunnable runnable; 40 | 41 | /** 42 | * Constructs a new RunnableAction without a pre-determined action. 43 | * 44 | * @param container the {@link StateContainer} responsible for this RunnableAction 45 | * @param type the type of {@link Action} being performed 46 | * @param goalState the destination for a {@link org.spongepowered.api.entity.living.player.Player} 47 | * after this Action is completed 48 | */ 49 | public RunnableAction(@Nonnull final StateContainer container, 50 | @Nonnull final ActionType type, 51 | @Nonnull final String goalState) { 52 | this(container, type, goalState, null); 53 | } 54 | 55 | /** 56 | * Constructs a new RunnableAction with a pre-determined action. 57 | * 58 | * @param container the {@link StateContainer} responsible for this RunnableAction 59 | * @param type the type of {@link Action} being performed 60 | * @param goalState the destination for a {@link org.spongepowered.api.entity.living.player.Player} 61 | * after this Action is completed 62 | * @param runnable the additional Action to be performed 63 | */ 64 | public RunnableAction(@Nonnull final StateContainer container, 65 | @Nonnull final ActionType type, 66 | @Nonnull final String goalState, 67 | @Nullable final UIRunnable runnable) { 68 | super(container, type, goalState); 69 | this.runnable = runnable; 70 | } 71 | 72 | /** 73 | * Gets the additional action to be performed. 74 | * 75 | * @return the additional actions to be performed 76 | */ 77 | @Nullable 78 | public UIRunnable getRunnable() { 79 | return this.runnable; 80 | } 81 | 82 | /** 83 | * Sets the additional actions to be performed. 84 | * 85 | * @param runnable the additional actions to be performed 86 | */ 87 | public void setRunnable(@Nonnull final UIRunnable runnable) { 88 | this.runnable = runnable; 89 | } 90 | 91 | /** 92 | * Runs the additional actions. 93 | * 94 | * @param currentState the current State before the Action is performed 95 | */ 96 | @Override 97 | public void runAction(@Nonnull final String currentState, Inventory inventory) { 98 | if (this.runnable != null) { 99 | this.runnable.run(this); 100 | } else { 101 | this.getObserver().sendMessage(Text.of(TextColors.RED, "Cannot run a null action!")); 102 | } 103 | super.runAction(currentState,inventory); 104 | } 105 | 106 | /** 107 | * Creates a copy of this RunnableAction. 108 | * 109 | * @param newContainer the new {@link StateContainer} to be responsible for this new Action 110 | * @return a copy of this RunnableAction 111 | */ 112 | @Nonnull 113 | @Override 114 | public RunnableAction copy(@Nonnull final StateContainer newContainer) { 115 | // UIRunnable doesn't need to be copied - it's just an action. 116 | return new RunnableAction(newContainer, this.getType(), this.getGoalState(), this.runnable); 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # HuskyUI 2 | A simple, lightweight UI system for, as of right now, chest GUIs. 3 | 4 | **[Java Docs](http://jd.codehusky.com/huskyui/)** 5 | 6 | **[Forum Topic](https://forums.spongepowered.org/t/huskyui-a-simple-fast-ui-system-for-plugins/19557/4)** 7 | 8 | **[HuskyUI Wiki](https://github.com/codeHusky/HuskyUI-Plugin/wiki)** 9 | 10 | # Implementation Examples 11 | 12 | ### Hotbar Compass using ElementRegistry 13 | 14 | ```java 15 | RunnableAction testAction = new RunnableAction(registry, ActionType.NONE,""); 16 | testAction.setRunnable(context -> { 17 | StateContainer container = new StateContainer(); 18 | Page testPage = Page.builder() 19 | .setTitle(Text.of(TextColors.GOLD,"Navigator")) 20 | .setAutoPaging(true) 21 | .addElement(new Element( 22 | ItemStack.builder() 23 | .itemType(ItemTypes.DIAMOND) 24 | .add(Keys.DISPLAY_NAME,Text.of(TextColors.BLUE,"Diamond Rush")) 25 | .build() 26 | )) 27 | .addElement(new Element( 28 | ItemStack.builder() 29 | .itemType(ItemTypes.FIREWORKS) 30 | .add(Keys.DISPLAY_NAME,Text.of(TextColors.RED,"Fireworks Palooza")) 31 | .build() 32 | )) 33 | .addElement(new Element( 34 | ItemStack.builder() 35 | .itemType(ItemTypes.MINECART) 36 | .add(Keys.DISPLAY_NAME,Text.of(TextColors.GRAY,"Roller Coasters")) 37 | .build() 38 | )) 39 | .build("testpage"); 40 | container.setInitialState(testPage); 41 | container.launchFor(context.getObserver()); 42 | }); 43 | ActionableElement testElement = new ActionableElement( 44 | testAction, 45 | ItemStack.builder() 46 | .itemType(ItemTypes.COMPASS) 47 | .add(Keys.DISPLAY_NAME, Text.of(TextColors.GOLD,"Navigator")) 48 | .build()); 49 | 50 | HuskyUI.getElementRegistry().registerAutoElement(4,testElement); 51 | ItemStack litMC = ItemStack.builder() 52 | .itemType(ItemTypes.REDSTONE_TORCH) 53 | .add(Keys.DISPLAY_NAME,Text.of(TextColors.RED,"LitMC")) 54 | .build(); 55 | HuskyUI.getElementRegistry().registerAutoElement(0,new Element(litMC)); 56 | HuskyUI.getElementRegistry().registerAutoElement(8,new Element(litMC)); 57 | 58 | HuskyUI.getElementRegistry().registerAutoElement(new Element(ItemStack.builder().itemType(ItemTypes.MINECART).add(Keys.DISPLAY_NAME,Text.of("movable 1")).build())); 59 | HuskyUI.getElementRegistry().registerAutoElement(new Element(ItemStack.builder().itemType(ItemTypes.MINECART).add(Keys.DISPLAY_NAME,Text.of("movable 2")).build())); 60 | HuskyUI.getElementRegistry().registerAutoElement(new Element(ItemStack.builder().itemType(ItemTypes.MINECART).add(Keys.DISPLAY_NAME,Text.of("movable 3")).build())); 61 | ``` 62 | **Result** (with some movement of the movable carts.) 63 | 64 | ![img](https://i.imgur.com/DvCy36o.png) 65 | 66 | ### Generic Example 67 | ```java 68 | StateContainer container = new StateContainer(); 69 | container.addState( 70 | Page.builder() 71 | .setUpdatable(true) 72 | .setUpdater(page -> { 73 | int count = 0; 74 | for(Inventory slot: page.getPageView().slots()){ 75 | 76 | if(!slot.peek().isPresent() && count == page.getTicks()%page.getPageView().capacity()){ 77 | slot.set(ItemStack.of(ItemTypes.STAINED_GLASS_PANE,count)); 78 | }else{ 79 | if(slot.peek().isPresent()) { 80 | ItemStack stack = slot.peek().get(); 81 | if (stack.getType() == ItemTypes.STAINED_GLASS_PANE) { 82 | slot.set(ItemStack.empty()); 83 | } 84 | } 85 | } 86 | count++; 87 | } 88 | }) 89 | .setUpdateTickRate(20) 90 | .setTitle(Text.of(TextColors.RED,"BLARG")) 91 | .addElement(new ActionableElement(new Action(container,ActionType.NORMAL,"test2"),ItemStack.builder(). 92 | itemType(ItemTypes.COOKIE) 93 | .build())) 94 | .build("test") 95 | ); 96 | container.addState( 97 | Page.builder() 98 | .setUpdatable(false) 99 | .setTitle(Text.of(TextColors.GREEN,"alt")) 100 | .addElement(new ActionableElement(new Action(container,ActionType.BACK,"test"),ItemStack.builder(). 101 | itemType(ItemTypes.COOKIE) 102 | .build())) 103 | .setParent("test") 104 | .build("test2") 105 | ); 106 | container.launchFor(plr); 107 | ``` 108 | -------------------------------------------------------------------------------- /src/main/java/com/codehusky/huskyui/states/State.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of HuskyUI. 3 | * 4 | * HuskyUI is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * HuskyUI is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with HuskyUI. If not, see . 16 | */ 17 | 18 | package com.codehusky.huskyui.states; 19 | 20 | import com.codehusky.huskyui.StateContainer; 21 | import org.spongepowered.api.entity.living.player.Player; 22 | import javax.annotation.Nonnull; 23 | 24 | /** 25 | * A State in the GUI process. Typically extended by {@link Page}. 26 | */ 27 | public class State { 28 | 29 | /** 30 | * The ID of this State. 31 | */ 32 | private final String id; 33 | 34 | /** 35 | * The ID of the parent to this State. 36 | */ 37 | private String parent; 38 | 39 | /** 40 | * The {@link StateContainer} responsible for this State. 41 | */ 42 | private StateContainer container; 43 | 44 | /** 45 | * The {@link Player}, if applicable, currently viewing this GUI. 46 | */ 47 | private Player observer; 48 | 49 | /** 50 | * An easily created way to construct a State. 51 | * 52 | *

This constructor will assume that the ID is "null", 53 | * and should really only be used for testing purposes.

54 | */ 55 | public State() { 56 | this("null"); 57 | } 58 | 59 | /** 60 | * A State constructor. Creates a State to be, typically, 61 | * stored in a {@link StateContainer}. 62 | * 63 | * @param id the ID of this State 64 | */ 65 | public State(@Nonnull final String id) { 66 | this.id = id; 67 | this.parent = null; 68 | this.container = null; 69 | this.observer = null; 70 | } 71 | 72 | /** 73 | * Gets the ID of this State. 74 | * 75 | * @return the ID of this State 76 | */ 77 | public String getId() { 78 | return this.id; 79 | } 80 | 81 | /** 82 | * Gets the ID of the parent to this State. 83 | * 84 | * @return the ID of the parent to this State 85 | */ 86 | public String getParent() { 87 | return this.parent; 88 | } 89 | 90 | /** 91 | * Determines whether or not this State has a parent. 92 | * 93 | * @return true if this State has a parent; false otherwise 94 | */ 95 | public boolean hasParent() { 96 | return this.parent != null; 97 | } 98 | 99 | /** 100 | * Sets the ID of the parent to this State. 101 | * 102 | * @param parent the ID of the parent to this State 103 | */ 104 | public void setParent(final String parent) { 105 | if(parent == null) return; 106 | this.parent = parent; 107 | } 108 | 109 | /** 110 | * Gets the {@link StateContainer} responsible for this State. 111 | * 112 | * @return the StateContainer responsible for this State 113 | */ 114 | public StateContainer getContainer() { 115 | return this.container; 116 | } 117 | 118 | /** 119 | * Sets the {@link StateContainer} responsible for this State. 120 | * 121 | * @param container the StateContainer responsible for this State 122 | */ 123 | public void setContainer(@Nonnull final StateContainer container) { 124 | this.container = container; 125 | } 126 | 127 | /** 128 | * Determines whether or not this State has a {@link StateContainer}. 129 | * 130 | *

Typically this would always be false, but people are crazy 131 | * so I dunno.

132 | * 133 | * @return true if this State has a StateContainer; false otherwise 134 | */ 135 | public boolean isHeadless() { 136 | return this.container == null; 137 | } 138 | 139 | /** 140 | * Gets the {@link Player} currently viewing this State, if applicable. 141 | * 142 | * @return the {@link Player} currently viewing this State 143 | */ 144 | public Player getObserver() { 145 | return this.observer; 146 | } 147 | 148 | /** 149 | * Determines whether or not this State is being viewed. 150 | * 151 | * @return true if this State is being viewed; false otherwise 152 | */ 153 | public boolean hasObserver() { 154 | return this.observer != null; 155 | } 156 | 157 | /** 158 | * Sets the {@link Player} who is viewing this State. 159 | * 160 | * @param observer the Player viewing this State 161 | */ 162 | public void setObserver(final Player observer) { 163 | this.observer = observer; 164 | } 165 | 166 | /** 167 | * Creates a copy of this State. 168 | * 169 | * @param newContainer the {@link StateContainer} that will be taking 170 | * responsibility for this State. 171 | * @return a copy of this State 172 | */ 173 | @Nonnull 174 | public State copy(@Nonnull final StateContainer newContainer) { 175 | final State state = new State(this.id); 176 | 177 | state.setContainer(newContainer); 178 | state.setObserver(this.observer); 179 | state.setParent(this.parent); 180 | 181 | return state; 182 | } 183 | } 184 | -------------------------------------------------------------------------------- /src/main/java/com/codehusky/huskyui/states/action/Action.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of HuskyUI. 3 | * 4 | * HuskyUI is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * HuskyUI is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with HuskyUI. If not, see . 16 | */ 17 | 18 | package com.codehusky.huskyui.states.action; 19 | 20 | import com.codehusky.huskyui.InventoryUtil; 21 | import com.codehusky.huskyui.StateContainer; 22 | import com.codehusky.huskyui.states.Page; 23 | import org.spongepowered.api.effect.sound.SoundTypes; 24 | import org.spongepowered.api.entity.living.player.Player; 25 | import org.spongepowered.api.item.inventory.Inventory; 26 | import org.spongepowered.api.text.Text; 27 | import org.spongepowered.api.text.format.TextColors; 28 | 29 | import javax.annotation.Nonnull; 30 | import java.util.function.Consumer; 31 | 32 | /** 33 | * This class' purpose is to be used to determine the movement 34 | * of a user within a GUI. 35 | * @see CommandAction 36 | * @see com.codehusky.huskyui.states.action.runnable.RunnableAction 37 | */ 38 | public class Action { 39 | 40 | /** 41 | * The {@link StateContainer} that is responsible for this Action. 42 | */ 43 | @Nonnull private final StateContainer container; 44 | 45 | /** 46 | * The {@link Player} observing the {@link com.codehusky.huskyui.states.Page} 47 | * that this action is being performed on. 48 | */ 49 | private Player observer = null; 50 | 51 | /** 52 | * The type of Action taking place. 53 | */ 54 | @Nonnull private final ActionType type; 55 | 56 | /** 57 | * The intended {@link com.codehusky.huskyui.states.State} that the 58 | * observer should find themselves in when this Action finishes. 59 | */ 60 | @Nonnull private final String goalState; 61 | 62 | /** 63 | * Constructs an Action. 64 | * 65 | * @param container the {@link StateContainer} that is responsible for this Action 66 | * @param type the type of Action taking place 67 | * @param goalState the intended {@link com.codehusky.huskyui.states.State} 68 | * for the observer to land 69 | */ 70 | public Action(@Nonnull final StateContainer container, 71 | @Nonnull final ActionType type, 72 | @Nonnull final String goalState) { 73 | this.container = container; 74 | this.type = type; 75 | this.goalState = goalState; 76 | } 77 | 78 | /** 79 | * Gets the {@link StateContainer} that is responsible for this Action. 80 | * 81 | * @return the StateContainer responsible for this Action 82 | */ 83 | @Nonnull 84 | public StateContainer getContainer() { 85 | return this.container; 86 | } 87 | 88 | /** 89 | * The {@link Player} that is observing this Action. 90 | * 91 | * @return the Player observing this Action 92 | */ 93 | public Player getObserver() { 94 | return this.observer; 95 | } 96 | 97 | /** 98 | * Sets the {@link Player} that is observing this Action 99 | * 100 | * @param observer the Player that is observing this Action 101 | */ 102 | public void setObserver(Player observer) { 103 | this.observer = observer; 104 | } 105 | 106 | /** 107 | * Gets the type of Action to be performed. 108 | * 109 | * @return the type of Action to be performed 110 | */ 111 | @Nonnull 112 | public ActionType getType() { 113 | return this.type; 114 | } 115 | 116 | /** 117 | * Gets the intended {@link com.codehusky.huskyui.states.State} for 118 | * the {@link Player} to end up in after this Action is performed. 119 | * 120 | * @return the intended State for the Player to end up in 121 | */ 122 | @Nonnull 123 | public String getGoalState() { 124 | return this.goalState; 125 | } 126 | 127 | /** 128 | * Performs this Action. 129 | * 130 | * @param currentState the current State before the Action is performed 131 | */ 132 | public void runAction(@Nonnull final String currentState, final Inventory inventory) { 133 | switch (this.type) { 134 | case CLOSE: 135 | InventoryUtil.close(this.observer); 136 | break; 137 | case BACK: 138 | if (this.container.hasState(currentState)) { 139 | if (this.container.getState(currentState).hasParent()) { 140 | this.container.openState(this.observer, this.container.getState(currentState).getParent()); 141 | } else { 142 | this.observer.playSound(SoundTypes.BLOCK_ANVIL_LAND, this.observer.getLocation().getPosition(), 0.5); 143 | InventoryUtil.close(this.observer); 144 | this.observer.sendMessage(Text.of(TextColors.RED, "Impossible BACK action - closing broken State.")); 145 | } 146 | } 147 | break; 148 | case NORMAL: 149 | this.container.openState(this.observer, this.goalState); 150 | break; 151 | case NONE: 152 | // do nothing 153 | break; 154 | case REFRESH: 155 | Page page = ((Page)getContainer().getState(currentState)); 156 | try { 157 | Consumer goo = page.getUpdateConsumer(); 158 | if (goo != null) { 159 | goo.accept(page); 160 | } 161 | }catch (Exception e){ 162 | this.observer.sendMessage(Text.of(TextColors.RED, "Impossible refresh action - closing broken State.")); 163 | } 164 | break; 165 | default: 166 | this.observer.sendMessage(Text.of("??")); 167 | break; 168 | } 169 | } 170 | 171 | /** 172 | * Creates a copy of this Action. 173 | * 174 | * @param newContainer the new {@link StateContainer} to be responsible for this new Action 175 | * @return a copy of this Action 176 | */ 177 | @Nonnull 178 | public Action copy(@Nonnull final StateContainer newContainer) { 179 | return new Action(newContainer, this.type, this.goalState); 180 | } 181 | } 182 | -------------------------------------------------------------------------------- /src/main/java/com/codehusky/huskyui/StateContainer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of HuskyUI. 3 | * 4 | * HuskyUI is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * HuskyUI is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with HuskyUI. If not, see . 16 | */ 17 | 18 | package com.codehusky.huskyui; 19 | 20 | import com.codehusky.huskyui.states.Page; 21 | import com.codehusky.huskyui.states.State; 22 | import com.google.common.collect.Maps; 23 | import org.spongepowered.api.entity.living.player.Player; 24 | import org.spongepowered.api.item.inventory.Inventory; 25 | import org.spongepowered.api.text.Text; 26 | import org.spongepowered.api.text.format.TextColors; 27 | 28 | import javax.annotation.Nonnull; 29 | import javax.annotation.Nullable; 30 | import java.util.Map; 31 | 32 | /** 33 | * The StateContainer's purpose is to be the gateway for information 34 | * regarding any active GUI. It can also be easily copied, with the 35 | * purpose of assigning a separate container for each user accessing 36 | * the GUI, allowing extensible on a per-user basis. 37 | */ 38 | public class StateContainer { 39 | 40 | /** 41 | * Contains the {@link State}s in use by this container, sorted 42 | * by that State's ID. 43 | */ 44 | @Nonnull private final Map states; 45 | 46 | /** 47 | * References the ID of the {@link State} which has been 48 | * deemed as the "default" State. 49 | * 50 | *

When the GUI is initially opened, for instance, this 51 | * ID should reference which State to be loaded first.

52 | */ 53 | @Nullable private String initialState; 54 | 55 | /** 56 | * A StateContainer constructor. Left empty to pass equally 57 | * empty data to the other constructor. 58 | */ 59 | public StateContainer() { 60 | this(Maps.newHashMap()); 61 | } 62 | 63 | /** 64 | * A StateContainer constructor. Passed to it are the current 65 | * {@link State}s that this GUI can expect to contain. 66 | * 67 | * @param states the current States for this GUI 68 | */ 69 | public StateContainer(@Nonnull final Map states) { 70 | this.states = states; 71 | this.initialState = null; 72 | } 73 | 74 | /** 75 | * A StateContainer constructor. Passed to it are the current 76 | * {@link State}s that this GUI can expect to contain. 77 | * 78 | *

Also requires an initial State to be defined.

79 | * 80 | * @param states the current States for this GUI 81 | * @param initialState the initial State 82 | */ 83 | public StateContainer(@Nonnull final Map states, @Nonnull final String initialState) { 84 | this.states = states; 85 | this.initialState = initialState; 86 | } 87 | 88 | /** 89 | * Gets the current {@link State}s in use by this GUI. 90 | * 91 | * @return the current States in use by this GUI 92 | */ 93 | @Nonnull 94 | public Map getStates() { 95 | return this.states; 96 | } 97 | 98 | /** 99 | * Gets a specific {@link State} in use by this GUI, 100 | * based on its ID. 101 | * 102 | * @param id the ID of the State being requested 103 | * @return the State being requested; null if non-existent 104 | */ 105 | @Nullable 106 | public State getState(@Nonnull final String id) { 107 | return this.states.get(id); 108 | } 109 | 110 | /** 111 | * Determines whether or not the requested {@link State} exists. 112 | * 113 | * @param id the ID of the State being determined 114 | * @return true if the State exists, false otherwise 115 | */ 116 | public boolean hasState(@Nonnull final String id) { 117 | return this.getState(id) != null; 118 | } 119 | 120 | /** 121 | * Adds a {@link State} to this GUI. 122 | * 123 | * If no default state has been set, this will also set the default state. 124 | * 125 | * @param state the State to be added 126 | */ 127 | public void addState(@Nonnull final State state) { 128 | state.setContainer(this); 129 | 130 | if (this.states.containsKey(state.getId())) { 131 | throw new IllegalStateException("A State with ID \"" + state.getId() + "\" already exists in this container."); 132 | } 133 | if(this.initialState == null){ 134 | this.initialState = state.getId(); 135 | } 136 | 137 | this.states.put(state.getId(), state); 138 | } 139 | 140 | /** 141 | * Removes a {@link State} from this GUI. 142 | * 143 | * @param id the ID of the State to be removed 144 | */ 145 | public void removeState(@Nonnull final String id) { 146 | if (this.initialState != null && this.initialState.equals(id)) { 147 | this.initialState = null; 148 | } 149 | 150 | this.states.remove(id); 151 | } 152 | 153 | /** 154 | * Removes a {@link State} from this GUI. 155 | * 156 | * @param state the State object itself to be removed 157 | */ 158 | public void removeState(@Nonnull final State state) { 159 | if (this.hasState(state.getId())) { 160 | this.removeState(state.getId()); 161 | } 162 | } 163 | 164 | 165 | /** 166 | * Sets the initial {@link State} to be displayed when 167 | * the GUI is opened. 168 | * 169 | * @param state the State to be displayed when the GUI is opened 170 | */ 171 | public void setInitialState(@Nonnull final State state) { 172 | this.initialState = state.getId(); 173 | 174 | if (!this.states.containsKey(state.getId())) { 175 | this.addState(state); 176 | } 177 | } 178 | 179 | /** 180 | * Opens a specific {@link State} for a {@link Player}, 181 | * based on the expected ID of the State. 182 | * 183 | * @param player the Player to display the State to 184 | * @param id the ID of the State being requested 185 | */ 186 | public void openState(@Nonnull final Player player, @Nonnull final String id) { 187 | int pagenum = 0; 188 | String pageID = id; 189 | if(id.contains("#")){ 190 | try { 191 | pagenum = Integer.valueOf(id.split("#")[1]); 192 | pageID=id.split("#")[0]; 193 | }catch(Exception e){ 194 | fail(player, "Attempted to open an invalid pagenum!"); 195 | } 196 | } 197 | State state = this.states.get(pageID); 198 | 199 | if (state == null) { 200 | fail(player, "Attempted to open a nonexistent state!"); 201 | fail(player, "Invalid ID: " + id); 202 | InventoryUtil.close(player); 203 | return; 204 | } 205 | state = state.copy(this); 206 | 207 | state.setObserver(player); 208 | 209 | if (state instanceof Page) { 210 | //InventoryUtil.close(player); 211 | Page page = (Page) state; 212 | Inventory toShow = page.getPageView(pagenum); 213 | InventoryUtil.open(player, toShow); 214 | 215 | return; 216 | } 217 | 218 | InventoryUtil.close(player); 219 | fail(player, "Attempted to open an invalid or incomplete state!"); 220 | fail(player, "Invalid ID: " + id); 221 | } 222 | 223 | 224 | /** 225 | * Opens the initial {@link State} for the {@link Player}. 226 | * 227 | * @param player the Player to show the initial State to 228 | */ 229 | public void launchFor(@Nonnull final Player player) { 230 | if (this.initialState == null) { 231 | fail(player, "Attempted to open a container without an initial state!"); 232 | return; 233 | } 234 | 235 | this.openState(player, this.initialState); 236 | } 237 | 238 | /** 239 | * Creates a copy of this StateContainer. 240 | * 241 | * @return a copy of this StateContainer 242 | */ 243 | @Nonnull 244 | public StateContainer copy() { 245 | final StateContainer container = new StateContainer(); 246 | 247 | for (final State state : this.states.values()) { 248 | container.addState(state.copy(container)); 249 | } 250 | 251 | if (this.initialState != null) { 252 | container.setInitialState(states.get(this.initialState)); 253 | } 254 | 255 | return container; 256 | } 257 | 258 | /** 259 | * A simple convenience method to send failures to 260 | * {@link Player}s; added by a misguided, lazy developer. 261 | * 262 | * @param player the Player to send the message to 263 | * @param message the message to be sent to the Player 264 | */ 265 | private static void fail(@Nonnull final Player player, @Nonnull final String message) { 266 | player.sendMessage(Text.of(TextColors.RED, message)); 267 | } 268 | } 269 | -------------------------------------------------------------------------------- /src/main/java/com/codehusky/huskyui/ElementRegistry.java: -------------------------------------------------------------------------------- 1 | package com.codehusky.huskyui; 2 | 3 | import com.codehusky.huskyui.states.element.ActionableElement; 4 | import com.codehusky.huskyui.states.element.Element; 5 | import org.spongepowered.api.data.DataQuery; 6 | import org.spongepowered.api.item.ItemTypes; 7 | import org.spongepowered.api.item.inventory.ItemStack; 8 | 9 | import java.util.ArrayList; 10 | import java.util.HashMap; 11 | import java.util.Optional; 12 | 13 | /** 14 | * ElementRegistry is a class only meant for use inside of HuskyUI to register elements with the plugin. 15 | * Using it for any other purpose is not advised. 16 | * This is for messing with Elements within player inventories. 17 | *

18 | * Auto-Inv and methods referencing Auto are talking about Auto-Inventory Delivery of the element. 19 | * Elements delivered to player inventories will be placed in the first available slot unless specified otherwise. 20 | *

21 | *

!! WARNING !!

22 | * Enforcing a slot id for an auto element will result on player's inventories being cleared every time they log in.
23 | * Please use this feature with care! By default, you should not do this. Make it controlled via config (default off) or be very explicit in disclosure of this feature.
24 | * Improper warning of this feature could be disastrous for some servers.
25 | * 26 | */ 27 | public class ElementRegistry extends StateContainer { 28 | 29 | private HashMap elements; 30 | 31 | //position, element id 32 | private HashMap autoInvLocations; 33 | private ArrayList autoInvElements; 34 | 35 | private int elementIncrement = 0; 36 | 37 | /** 38 | * Initialization for Registry. 39 | * For use only by the HuskyUI main class. 40 | */ 41 | ElementRegistry() { 42 | elements = new HashMap<>(); 43 | autoInvLocations = new HashMap<>(); 44 | autoInvElements = new ArrayList<>(); 45 | } 46 | 47 | 48 | /** 49 | * Register an element to HuskyUI's Element Registry. 50 | * 51 | * @param element Element to register 52 | * @return elementID of registered element 53 | */ 54 | public int registerElement(Element element){ 55 | int key = elementIncrement; 56 | elements.put(key,element); 57 | elementIncrement++; 58 | return key; 59 | } 60 | 61 | /** 62 | * Unregister an element from the Registry.
63 | * Note that this will not remove an item from a player registered to this ID, so use of this method is heavily discouraged. 64 | * 65 | * @param id Element to unregister 66 | */ 67 | public void unregisterElement(int id){ 68 | if(elements.containsKey(id)){ 69 | elements.remove(id); 70 | if(autoInvElements.contains(id)){ 71 | autoInvElements.remove(id); 72 | if(autoInvLocations.values().contains(id)){ 73 | autoInvLocations.values().removeIf(val -> id == val); 74 | } 75 | } 76 | }else{ 77 | throw new RuntimeException("Cannot unregister element: Element id \"" + id + "\" is not registered."); 78 | } 79 | } 80 | 81 | /** 82 | * Register an element to the Registry and also the auto-deployment system.
83 | * Items registered in this way will be given to the player when they log into the server. 84 | * 85 | * @param element Element to register 86 | * @return elementID of registered element. 87 | */ 88 | public int registerAutoElement(Element element){ 89 | int id = registerElement(element); 90 | autoInvElements.add(id); 91 | return id; 92 | } 93 | 94 | /** 95 | * Register an existing element to the ElementRegistry
96 | * @param elementID ElementID to register with auto-inv delivery enabled 97 | */ 98 | public void registerAutoElement(int elementID){ 99 | if(!elements.containsKey(elementID)){ 100 | throw new RuntimeException("Cannot register element as auto: Element id \"" + elementID + "\" is not registered."); 101 | } 102 | if(autoInvElements.contains(elementID)) return; 103 | autoInvElements.add(elementID); 104 | } 105 | 106 | /** 107 | * Registers an element at a given fixed slot position.

108 | * WARNING: Adding anything to a fixed slot ID will result in the removal of all user items on their spawn.
109 | * Please make use of this method configurable in your plugin, with it being disabled by default. Thanks! 110 | * 111 | * @param slotID SlotID to associate element with 112 | * @param element Element to register 113 | * @return ElementID 114 | */ 115 | public int registerAutoElement(int slotID, Element element){ 116 | int id = registerElement(element); 117 | autoInvLocations.put(slotID,id); 118 | autoInvElements.add(id); 119 | return id; 120 | } 121 | 122 | /** 123 | * Associates an existing element to a given fixed slot position.

124 | * WARNING: Adding anything to a fixed slot ID will result in the removal of all user items on their spawn.
125 | * Please make use of this method configurable in your plugin, with it being disabled by default. Thanks! 126 | * 127 | * @param slotID SlotID to associate element with 128 | * @param elementID ElementID to bind to slot. 129 | */ 130 | public void registerAutoElement(int slotID, int elementID){ 131 | if(!elements.containsKey(elementID)){ 132 | throw new RuntimeException("Cannot register element as auto: Element id \"" + elementID + "\" is not registered."); 133 | } 134 | autoInvLocations.put(slotID,elementID); 135 | 136 | if(!autoInvElements.contains(elementID)) 137 | autoInvElements.add(elementID); 138 | } 139 | 140 | /** 141 | * Get all auto-given items (placed in user inventory on login) 142 | * @return HashMap of ElementIDs to Element itemstacks. 143 | */ 144 | public HashMap getAutoItems() { 145 | HashMap stacks = new HashMap<>(); 146 | for(int i : autoInvElements){ 147 | stacks.put(i,getItemStackForElement(i)); 148 | } 149 | return stacks; 150 | } 151 | 152 | /** 153 | * Get all fixed locations of auto items.
154 | * If this hashmap is empty, inventories are NOT reset on login. 155 | * 156 | * @return HashMap containing SlotIDs to ElementIDs 157 | */ 158 | public HashMap getAutoItemLocations() { 159 | return autoInvLocations; 160 | } 161 | 162 | /** 163 | * Convert an ItemStack into an Element 164 | * 165 | * @param stack ItemStack to pull Element from 166 | * @return Element, if it exists with that item. 167 | */ 168 | public Optional getElementFromItemStack(ItemStack stack){ 169 | Optional optID = getElementIDFromItemStack(stack); 170 | if(optID.isPresent()){ 171 | if(elements.containsKey(optID.get())){ 172 | return Optional.of(elements.get(optID.get())); 173 | } 174 | } 175 | return Optional.empty(); 176 | } 177 | 178 | /** 179 | * Convert an ItemStack into an ElementID 180 | * 181 | * @param stack ItemStack to pull ElementID from 182 | * @return ElementID, if it exists. This ID may not actually be associated with an Element, so please verify that before use with {@link #elementExists(int)}. 183 | */ 184 | public Optional getElementIDFromItemStack(ItemStack stack){ 185 | if(stack.getType() == ItemTypes.AIR || stack.getType() == ItemTypes.NONE) return Optional.empty(); 186 | Optional optRegID = stack.toContainer().get(DataQuery.of("UnsafeData", "regid")); 187 | if(optRegID.isPresent()){ 188 | return Optional.of((int)optRegID.get()); 189 | } 190 | return Optional.empty(); 191 | } 192 | 193 | /** 194 | * Verify that an ElementID actually is registered to an Element 195 | * 196 | * @param id ElementID to check against 197 | * @return If element exists under that id 198 | */ 199 | public boolean elementExists(int id){ 200 | return elements.containsKey(id); 201 | } 202 | 203 | /** 204 | * Check to see if an element at a given id is auto-inv 205 | * 206 | * @param id element id 207 | * @return if element is auto-inv 208 | */ 209 | public boolean isElementAuto(int id){ 210 | if(!elements.containsKey(id)) throw new RuntimeException("Cannot check if element is auto: Element id \"" + id + "\" is not registered."); 211 | return autoInvElements.contains(id); 212 | } 213 | 214 | /** 215 | * Check to see if an element is in a fixed position
216 | * Accessing this with a non-auto-inv item will throw a runtime exception. 217 | * 218 | * @param id element id 219 | * @return if element is fixed position 220 | */ 221 | public boolean isElementFixedAuto(int id){ 222 | if(!isElementAuto(id)) throw new RuntimeException("Cannot check if element is fixed auto: Element \"" + id + "\"is not auto."); 223 | return autoInvLocations.values().contains(id); 224 | } 225 | 226 | /** 227 | * Get an inventory-ready itemstack for any given element 228 | * 229 | * @param elementID ElementID to get an itemstack of. 230 | * @return ItemStack of given element. 231 | */ 232 | public ItemStack getItemStackForElement(int elementID){ 233 | if(elements.containsKey(elementID)){ 234 | return ItemStack.builder() 235 | .fromContainer(elements.get(elementID) 236 | .getItem() 237 | .toContainer() 238 | .set(DataQuery.of("UnsafeData", "regid"), elementID)) 239 | .build(); 240 | } 241 | throw new RuntimeException("Cannot get ItemStack: Element id \"" + elementID + "\" is not registered."); 242 | } 243 | } 244 | -------------------------------------------------------------------------------- /src/main/java/com/codehusky/huskyui/HuskyUI.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of HuskyUI. 3 | * 4 | * HuskyUI is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * HuskyUI is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with HuskyUI. If not, see . 16 | */ 17 | 18 | package com.codehusky.huskyui; 19 | 20 | import com.codehusky.huskyui.states.element.ActionableElement; 21 | import com.codehusky.huskyui.states.element.Element; 22 | import org.slf4j.Logger; 23 | import org.slf4j.LoggerFactory; 24 | import org.spongepowered.api.entity.Entity; 25 | import org.spongepowered.api.entity.Item; 26 | import org.spongepowered.api.entity.living.player.Player; 27 | import org.spongepowered.api.event.Listener; 28 | import org.spongepowered.api.event.Order; 29 | import org.spongepowered.api.event.cause.Cause; 30 | import org.spongepowered.api.event.item.inventory.ClickInventoryEvent; 31 | import org.spongepowered.api.event.item.inventory.DropItemEvent; 32 | import org.spongepowered.api.event.item.inventory.InteractItemEvent; 33 | import org.spongepowered.api.event.item.inventory.UseItemStackEvent; 34 | import org.spongepowered.api.event.network.ClientConnectionEvent; 35 | import org.spongepowered.api.item.ItemTypes; 36 | import org.spongepowered.api.item.inventory.Inventory; 37 | import org.spongepowered.api.item.inventory.InventoryArchetypes; 38 | import org.spongepowered.api.item.inventory.ItemStack; 39 | import org.spongepowered.api.plugin.Plugin; 40 | import org.spongepowered.api.plugin.PluginContainer; 41 | 42 | import javax.annotation.Nonnull; 43 | import javax.inject.Inject; 44 | import java.util.HashMap; 45 | import java.util.Optional; 46 | 47 | /** 48 | * The HuskyUI class that gets loaded by Sponge at runtime. 49 | * 50 | *

Currently this class serves no real purpose except as 51 | * an easily accessible container for versioning and logging 52 | * data.

53 | */ 54 | @Plugin(id = HuskyUI.PLUGIN_ID, name = HuskyUI.PLUGIN_NAME, version = HuskyUI.PLUGIN_VERSION, 55 | description = "A framework for Inventory UI.") 56 | public class HuskyUI { 57 | 58 | /** 59 | * The ID of HuskyUI for Sponge. 60 | */ 61 | public static final String PLUGIN_ID = "huskyui"; 62 | 63 | /** 64 | * The Name of HuskyUI for Sponge. 65 | */ 66 | public static final String PLUGIN_NAME = "HuskyUI"; 67 | 68 | /** 69 | * The Version of HuskyUI for Sponge. 70 | */ 71 | public static final String PLUGIN_VERSION = "0.6.0PRE4"; 72 | 73 | /** 74 | * The HuskyUI {@link Logger} used throughout the plugin. 75 | */ 76 | private static final Logger LOGGER = LoggerFactory.getLogger(HuskyUI.class); 77 | 78 | 79 | private final ElementRegistry registry = new ElementRegistry(); 80 | 81 | /** 82 | * Contains a reference to this (soft) singleton class. 83 | */ 84 | private static HuskyUI instance; 85 | 86 | /** 87 | * References the {@link PluginContainer} that Sponge 88 | * creates for HuskyUI. 89 | */ 90 | @Nonnull private final PluginContainer pluginContainer; 91 | 92 | /** 93 | * The main, generic {@link Cause} that gets passed to 94 | * Sponge during (or while creating) events. 95 | */ 96 | private Cause genericCause; 97 | 98 | /** 99 | * The HuskyUI constructor. 100 | * 101 | *

Is intended to be called by Sponge's classloader.

102 | * 103 | * @param pluginContainer the container for HuskyUI passed by Sponge 104 | */ 105 | @Inject 106 | public HuskyUI(@Nonnull final PluginContainer pluginContainer) { 107 | HuskyUI.instance = this; 108 | this.pluginContainer = pluginContainer; 109 | } 110 | 111 | /** 112 | * Gets an instance of HuskyUI. 113 | * 114 | * @return an instance of HuskyUI 115 | */ 116 | public static HuskyUI getInstance() { 117 | return HuskyUI.instance; 118 | } 119 | 120 | /** 121 | * Gets HuskyUI's plugin container created by Sponge. 122 | * 123 | * @return HuskyUI's plugin container 124 | */ 125 | @Nonnull 126 | public PluginContainer getPluginContainer() { 127 | return this.pluginContainer; 128 | } 129 | 130 | 131 | /** 132 | * Gets the {@link Logger} used by HuskyUI. 133 | * 134 | * @return the Logger used by HuskyUI 135 | */ 136 | public static Logger getLogger() { 137 | return LOGGER; 138 | } 139 | 140 | /** 141 | * 142 | * @return HuskyUI Global Element Registry 143 | */ 144 | public ElementRegistry getElementRegistry() { 145 | return registry; 146 | } 147 | 148 | /*@Listener 149 | public void serverStart(GameStartedServerEvent event){ 150 | RunnableAction testAction = new RunnableAction(registry, ActionType.NONE,""); 151 | testAction.setRunnable(context -> { 152 | StateContainer container = new StateContainer(); 153 | Page testPage = Page.builder() 154 | .setTitle(Text.of(TextColors.GOLD,"Navigator")) 155 | .setAutoPaging(true) 156 | .addElement(new Element( 157 | ItemStack.builder() 158 | .itemType(ItemTypes.DIAMOND) 159 | .add(Keys.DISPLAY_NAME,Text.of(TextColors.BLUE,"Diamond Rush")) 160 | .build() 161 | )) 162 | .addElement(new Element( 163 | ItemStack.builder() 164 | .itemType(ItemTypes.FIREWORKS) 165 | .add(Keys.DISPLAY_NAME,Text.of(TextColors.RED,"Fireworks Palooza")) 166 | .build() 167 | )) 168 | .addElement(new ActionableElement(new RunnableAction(container, ActionType.NONE, "", new UIRunnable() { 169 | @Override 170 | public void run(@Nonnull RunnableAction context) { 171 | context.getContainer().openState(context.getObserver(),"satan"); 172 | } 173 | }), 174 | ItemStack.builder() 175 | .itemType(ItemTypes.MINECART) 176 | .add(Keys.DISPLAY_NAME,Text.of(TextColors.GRAY,"Roller Coasters")) 177 | .build() 178 | )) 179 | .build("testpage"); 180 | Page lilMeme = Page.builder() 181 | .setTitle(Text.of("sata")) 182 | .setAutoPaging(true) 183 | .addElement(new Element(ItemStack.of(ItemTypes.GLASS,1))) 184 | .addElement(new Element(ItemStack.of(ItemTypes.GLASS,1))) 185 | .addElement(new Element(ItemStack.of(ItemTypes.GLASS,1))) 186 | .addElement(new Element(ItemStack.of(ItemTypes.GLASS,1))) 187 | .addElement(new Element(ItemStack.of(ItemTypes.GLASS,1))) 188 | .addElement(new Element(ItemStack.of(ItemTypes.GLASS,1))) 189 | .addElement(new Element(ItemStack.of(ItemTypes.GLASS,1))) 190 | .addElement(new Element(ItemStack.of(ItemTypes.GLASS,1))) 191 | .addElement(new Element(ItemStack.of(ItemTypes.GLASS,1))) 192 | .addElement(new Element(ItemStack.of(ItemTypes.GLASS,1))) 193 | //\\ 194 | .addElement(new Element(ItemStack.of(ItemTypes.GLASS,1))) 195 | .addElement(new Element(ItemStack.of(ItemTypes.GLASS,1))) 196 | .addElement(new Element(ItemStack.of(ItemTypes.GLASS,1))) 197 | .addElement(new Element(ItemStack.of(ItemTypes.GLASS,1))) 198 | .addElement(new Element(ItemStack.of(ItemTypes.GLASS,1))) 199 | .addElement(new Element(ItemStack.of(ItemTypes.GLASS,1))) 200 | .addElement(new Element(ItemStack.of(ItemTypes.GLASS,1))) 201 | .addElement(new Element(ItemStack.of(ItemTypes.GLASS,1))) 202 | .addElement(new Element(ItemStack.of(ItemTypes.GLASS,1))) 203 | .addElement(new Element(ItemStack.of(ItemTypes.GLASS,1))) 204 | //\\ 205 | .addElement(new Element(ItemStack.of(ItemTypes.GLASS,1))) 206 | .addElement(new Element(ItemStack.of(ItemTypes.GLASS,1))) 207 | .addElement(new Element(ItemStack.of(ItemTypes.GLASS,1))) 208 | .addElement(new Element(ItemStack.of(ItemTypes.GLASS,1))) 209 | .addElement(new Element(ItemStack.of(ItemTypes.GLASS,1))) 210 | .addElement(new Element(ItemStack.of(ItemTypes.GLASS,1))) 211 | .addElement(new Element(ItemStack.of(ItemTypes.GLASS,1))) 212 | .addElement(new Element(ItemStack.of(ItemTypes.GLASS,1))) 213 | .addElement(new Element(ItemStack.of(ItemTypes.GLASS,1))) 214 | .addElement(new Element(ItemStack.of(ItemTypes.GLASS,1))) 215 | //\\ 216 | .addElement(new Element(ItemStack.of(ItemTypes.GLASS,1))) 217 | .addElement(new Element(ItemStack.of(ItemTypes.GLASS,1))) 218 | .addElement(new Element(ItemStack.of(ItemTypes.GLASS,1))) 219 | .addElement(new Element(ItemStack.of(ItemTypes.GLASS,1))) 220 | .addElement(new Element(ItemStack.of(ItemTypes.GLASS,1))) 221 | .addElement(new Element(ItemStack.of(ItemTypes.GLASS,1))) 222 | .addElement(new Element(ItemStack.of(ItemTypes.GLASS,1))) 223 | .addElement(new Element(ItemStack.of(ItemTypes.GLASS,1))) 224 | .addElement(new Element(ItemStack.of(ItemTypes.GLASS,1))) 225 | .addElement(new Element(ItemStack.of(ItemTypes.GLASS,1))) 226 | //\\ 227 | .addElement(new Element(ItemStack.of(ItemTypes.GLASS,1))) 228 | .addElement(new Element(ItemStack.of(ItemTypes.GLASS,1))) 229 | .addElement(new Element(ItemStack.of(ItemTypes.GLASS,1))) 230 | .addElement(new Element(ItemStack.of(ItemTypes.GLASS,1))) 231 | .addElement(new Element(ItemStack.of(ItemTypes.GLASS,1))) 232 | .addElement(new Element(ItemStack.of(ItemTypes.GLASS,1))) 233 | .addElement(new Element(ItemStack.of(ItemTypes.GLASS,1))) 234 | .addElement(new Element(ItemStack.of(ItemTypes.GLASS,1))) 235 | .addElement(new Element(ItemStack.of(ItemTypes.GLASS,1))) 236 | .addElement(new Element(ItemStack.of(ItemTypes.GLASS,1))) 237 | //\\ 238 | .addElement(new Element(ItemStack.of(ItemTypes.GLASS,1))) 239 | .addElement(new Element(ItemStack.of(ItemTypes.GLASS,1))) 240 | .addElement(new Element(ItemStack.of(ItemTypes.GLASS,1))) 241 | .addElement(new Element(ItemStack.of(ItemTypes.GLASS,1))) 242 | .addElement(new Element(ItemStack.of(ItemTypes.GLASS,1))) 243 | .addElement(new Element(ItemStack.of(ItemTypes.GLASS,1))) 244 | .addElement(new Element(ItemStack.of(ItemTypes.GLASS,1))) 245 | .addElement(new Element(ItemStack.of(ItemTypes.GLASS,1))) 246 | .addElement(new Element(ItemStack.of(ItemTypes.GLASS,1))) 247 | .addElement(new Element(ItemStack.of(ItemTypes.GLASS,1))) 248 | //\\ 249 | .addElement(new Element(ItemStack.of(ItemTypes.GLASS,1))) 250 | .addElement(new Element(ItemStack.of(ItemTypes.GLASS,1))) 251 | .addElement(new Element(ItemStack.of(ItemTypes.GLASS,1))) 252 | .addElement(new Element(ItemStack.of(ItemTypes.GLASS,1))) 253 | .addElement(new Element(ItemStack.of(ItemTypes.GLASS,1))) 254 | .addElement(new Element(ItemStack.of(ItemTypes.GLASS,1))) 255 | .addElement(new Element(ItemStack.of(ItemTypes.GLASS,1))) 256 | .addElement(new Element(ItemStack.of(ItemTypes.GLASS,1))) 257 | .addElement(new Element(ItemStack.of(ItemTypes.GLASS,1))) 258 | .addElement(new Element(ItemStack.of(ItemTypes.GLASS,1))) 259 | //\\ 260 | .addElement(new Element(ItemStack.of(ItemTypes.GLASS,1))) 261 | .addElement(new Element(ItemStack.of(ItemTypes.GLASS,1))) 262 | .addElement(new Element(ItemStack.of(ItemTypes.GLASS,1))) 263 | .addElement(new Element(ItemStack.of(ItemTypes.GLASS,1))) 264 | .addElement(new Element(ItemStack.of(ItemTypes.GLASS,1))) 265 | .addElement(new Element(ItemStack.of(ItemTypes.GLASS,1))) 266 | .addElement(new Element(ItemStack.of(ItemTypes.GLASS,1))) 267 | .addElement(new Element(ItemStack.of(ItemTypes.GLASS,1))) 268 | .addElement(new Element(ItemStack.of(ItemTypes.GLASS,1))) 269 | .addElement(new Element(ItemStack.of(ItemTypes.GLASS,1))) 270 | //\\ 271 | .addElement(new Element(ItemStack.of(ItemTypes.GLASS,1))) 272 | .addElement(new Element(ItemStack.of(ItemTypes.GLASS,1))) 273 | .addElement(new Element(ItemStack.of(ItemTypes.GLASS,1))) 274 | .addElement(new Element(ItemStack.of(ItemTypes.GLASS,1))) 275 | .addElement(new Element(ItemStack.of(ItemTypes.GLASS,1))) 276 | .addElement(new Element(ItemStack.of(ItemTypes.GLASS,1))) 277 | .addElement(new Element(ItemStack.of(ItemTypes.GLASS,1))) 278 | .addElement(new Element(ItemStack.of(ItemTypes.GLASS,1))) 279 | .addElement(new Element(ItemStack.of(ItemTypes.GLASS,1))) 280 | .addElement(new Element(ItemStack.of(ItemTypes.GLASS,1))) 281 | //\\ 282 | .addElement(new Element(ItemStack.of(ItemTypes.GLASS,1))) 283 | .addElement(new Element(ItemStack.of(ItemTypes.GLASS,1))) 284 | .addElement(new Element(ItemStack.of(ItemTypes.GLASS,1))) 285 | .addElement(new Element(ItemStack.of(ItemTypes.GLASS,1))) 286 | .addElement(new Element(ItemStack.of(ItemTypes.GLASS,1))) 287 | .addElement(new Element(ItemStack.of(ItemTypes.GLASS,1))) 288 | .addElement(new Element(ItemStack.of(ItemTypes.GLASS,1))) 289 | .addElement(new Element(ItemStack.of(ItemTypes.GLASS,1))) 290 | .addElement(new Element(ItemStack.of(ItemTypes.GLASS,1))) 291 | .addElement(new Element(ItemStack.of(ItemTypes.GLASS,1))) 292 | //\\ 293 | .setParent("testpage") 294 | .build("satan"); 295 | container.setInitialState(testPage); 296 | container.addState(lilMeme); 297 | container.launchFor(context.getObserver()); 298 | }); 299 | ActionableElement testElement = new ActionableElement( 300 | testAction, 301 | ItemStack.builder() 302 | .itemType(ItemTypes.COMPASS) 303 | .add(Keys.DISPLAY_NAME, Text.of(TextColors.GOLD,"Navigator")) 304 | .build()); 305 | 306 | registry.registerAutoElement(29,testElement); 307 | ItemStack litMC = ItemStack.builder() 308 | .itemType(ItemTypes.REDSTONE_TORCH) 309 | .add(Keys.DISPLAY_NAME,Text.of(TextColors.RED,"LitMC")) 310 | .build(); 311 | registry.registerAutoElement(31,new Element(litMC)); 312 | registry.registerAutoElement(35,new Element(litMC)); 313 | 314 | registry.registerAutoElement(new Element(ItemStack.builder().itemType(ItemTypes.MINECART).add(Keys.DISPLAY_NAME,Text.of("movable 1")).build())); 315 | registry.registerAutoElement(new Element(ItemStack.builder().itemType(ItemTypes.MINECART).add(Keys.DISPLAY_NAME,Text.of("movable 2")).build())); 316 | registry.registerAutoElement(new Element(ItemStack.builder().itemType(ItemTypes.MINECART).add(Keys.DISPLAY_NAME,Text.of("movable 3")).build())); 317 | }*/ 318 | 319 | /** 320 | * Handle auto-item delivery to player. 321 | * 322 | * @param event ClientConnectionEvent Join, when player is created into world. 323 | */ 324 | @Listener 325 | public void onPlayerSpawn(ClientConnectionEvent.Join event){ 326 | HashMap items = registry.getAutoItems(); 327 | HashMap itemPositions = registry.getAutoItemLocations(); 328 | if(itemPositions.size() > 0) { 329 | event.getTargetEntity().getInventory().clear(); 330 | int slotNum = 0; 331 | for (Inventory slot : event.getTargetEntity().getInventory().slots()) { 332 | if (itemPositions.containsKey(slotNum)) { 333 | slot.set(items.get(itemPositions.get(slotNum))); 334 | } 335 | slotNum++; 336 | } 337 | for(int elementID: items.keySet()){ 338 | if(!itemPositions.containsValue(elementID)){ 339 | event.getTargetEntity().getInventory().offer(items.get(elementID)); 340 | } 341 | } 342 | }else{ 343 | int slotNum = 0; 344 | for (Inventory slot : event.getTargetEntity().getInventory().slots()) { 345 | if (slot.peek().isPresent()) { 346 | Optional eleID = registry.getElementIDFromItemStack(slot.peek().get()); 347 | if(eleID.isPresent()){ 348 | slot.clear(); 349 | } 350 | } 351 | slotNum++; 352 | } 353 | for(int elementID: items.keySet()){ 354 | if(!itemPositions.containsValue(elementID)){ 355 | event.getTargetEntity().getInventory().offer(items.get(elementID)); 356 | } 357 | } 358 | } 359 | 360 | } 361 | 362 | /** 363 | * Handler for ActionableElement actions. 364 | * 365 | * @param event When a player interacts with an item with their secondary mouse button. 366 | */ 367 | @Listener(order= Order.LAST) 368 | public void onElementInteract(InteractItemEvent event){ 369 | Optional ele = registry.getElementFromItemStack(event.getItemStack().createStack()); 370 | if(ele.isPresent()){ 371 | if(event instanceof InteractItemEvent.Secondary) { 372 | if (ele.get() instanceof ActionableElement) { 373 | ActionableElement aElement = ((ActionableElement) ele.get()).copy(registry); 374 | aElement.getAction().setObserver((Player) event.getCause().root()); 375 | aElement.getAction().runAction("",null); 376 | } 377 | } 378 | 379 | event.setCancelled(true); 380 | } 381 | } 382 | 383 | 384 | /** 385 | * Handle item drops 386 | * @param event dispense event 387 | */ 388 | @Listener 389 | public void onItemDrop(DropItemEvent.Dispense event){ 390 | for(Entity e :event.getEntities()){ 391 | if(e instanceof Item){ 392 | ItemStack affectedStack = ((Item) e).getItemData().item().get().createStack(); 393 | Optional potentialID = registry.getElementIDFromItemStack(affectedStack); 394 | if(potentialID.isPresent()){ 395 | if(registry.elementExists(potentialID.get())){ 396 | event.setCancelled(true); //NOTHING should drop a registered item. >:( 397 | //TODO: handle https://github.com/SpongePowered/SpongeCommon/issues/1678 properly w/ workaround 398 | } 399 | } 400 | } 401 | } 402 | } 403 | 404 | /** 405 | * Handle item usage 406 | * @param event useitemstackevent.start 407 | */ 408 | @Listener 409 | public void onItemUse(UseItemStackEvent.Start event){ 410 | Optional potentialID = registry.getElementIDFromItemStack(event.getItemStackInUse().createStack()); 411 | if(potentialID.isPresent()){ 412 | if(registry.elementExists(potentialID.get())){ 413 | event.setCancelled(true); 414 | } 415 | } 416 | } 417 | 418 | /** 419 | * Handle inventory clicks 420 | * @param event clickinvevent 421 | */ 422 | @Listener(order = Order.PRE) 423 | public void onItemClick(ClickInventoryEvent event){ 424 | try { 425 | if (event instanceof ClickInventoryEvent.Primary || 426 | event instanceof ClickInventoryEvent.Secondary || 427 | event instanceof ClickInventoryEvent.Shift || 428 | event instanceof ClickInventoryEvent.Creative) { 429 | 430 | ItemStack affected; 431 | affected = event.getTransactions().get(0).getOriginal().createStack(); 432 | if (event instanceof ClickInventoryEvent.Shift && (affected.getType() == ItemTypes.AIR || affected.getType() == ItemTypes.NONE)) { 433 | affected = event.getTransactions().get(0).getDefault().createStack(); 434 | } 435 | Optional potentialID = registry.getElementIDFromItemStack(affected); 436 | if (potentialID.isPresent()) { 437 | if (registry.elementExists(potentialID.get())) { 438 | if (registry.isElementAuto(potentialID.get())) { 439 | if (event.getTransactions().get(0).getSlot().parent().getArchetype().equals(InventoryArchetypes.PLAYER)) { 440 | if (registry.isElementFixedAuto(potentialID.get())) { 441 | event.setCancelled(true); 442 | } 443 | } else { 444 | event.setCancelled(true); 445 | } 446 | } 447 | } 448 | } 449 | } 450 | }catch(Exception ignored){} 451 | } 452 | 453 | 454 | } 455 | -------------------------------------------------------------------------------- /src/main/java/com/codehusky/huskyui/states/Page.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of HuskyUI. 3 | * 4 | * HuskyUI is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * HuskyUI is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with HuskyUI. If not, see . 16 | */ 17 | 18 | package com.codehusky.huskyui.states; 19 | 20 | import com.codehusky.huskyui.HuskyUI; 21 | import com.codehusky.huskyui.InventoryUtil; 22 | import com.codehusky.huskyui.StateContainer; 23 | import com.codehusky.huskyui.states.element.ActionableElement; 24 | import com.codehusky.huskyui.states.element.Element; 25 | import com.google.common.collect.Maps; 26 | import org.spongepowered.api.data.DataQuery; 27 | import org.spongepowered.api.data.key.Keys; 28 | import org.spongepowered.api.data.type.DyeColors; 29 | import org.spongepowered.api.entity.living.player.Player; 30 | import org.spongepowered.api.event.item.inventory.InteractInventoryEvent; 31 | import org.spongepowered.api.item.ItemTypes; 32 | import org.spongepowered.api.item.inventory.*; 33 | import org.spongepowered.api.item.inventory.property.InventoryDimension; 34 | import org.spongepowered.api.item.inventory.property.InventoryTitle; 35 | import org.spongepowered.api.item.inventory.property.StringProperty; 36 | import org.spongepowered.api.scheduler.Task; 37 | import org.spongepowered.api.text.Text; 38 | import org.spongepowered.api.text.format.TextColors; 39 | import org.spongepowered.api.text.format.TextStyles; 40 | 41 | import javax.annotation.Nonnull; 42 | import javax.annotation.Nullable; 43 | import java.util.Map; 44 | import java.util.function.Consumer; 45 | 46 | /** 47 | * An extension of {@link State}, intended to be used for 48 | * chest-based GUIs. 49 | */ 50 | public class Page extends State { 51 | 52 | /** 53 | * The default {@link ItemStack} to be used if no ItemStack is defined 54 | * for a slot while {@link Page#fillWhenEmpty} is set to true. 55 | */ 56 | @Nonnull public static ItemStack defaultEmptyStack = ItemStack.builder() 57 | .itemType(ItemTypes.STAINED_GLASS_PANE) 58 | .add(Keys.DYE_COLOR, DyeColors.BLACK) 59 | .add(Keys.DISPLAY_NAME, Text.of(TextColors.DARK_GRAY, "HuskyUI")).build(); 60 | 61 | /** 62 | * The {@link Element}s that will be used by this Page. 63 | * 64 | *

Elements are sorted by integer, ostensibly referring 65 | * to their placement in the chest.

66 | */ 67 | @Nonnull private final Map elements; 68 | 69 | /** 70 | * The {@link InventoryDimension} is the space in which {@link Element}s 71 | * will be placed when the inventory is opened by a {@link Player}. 72 | */ 73 | @Nonnull private final InventoryDimension inventoryDimension; 74 | 75 | @Nonnull private final InventoryArchetype inventoryArchetype; 76 | 77 | /** 78 | * The name of the Chest to be opened, because IDs 79 | * typically aren't very visually appealing. 80 | */ 81 | @Nonnull private final Text title; 82 | 83 | /** 84 | * The {@link ItemStack} to be used when {@link Page#fillWhenEmpty} 85 | * is set to true. Usually this will be determined by 86 | * {@link Page#defaultEmptyStack}. 87 | */ 88 | @Nonnull private final ItemStack emptyStack; 89 | 90 | /** 91 | * Whether or not to fill empty stacks with 92 | * predetermined {@link ItemStack}s. 93 | */ 94 | private final boolean fillWhenEmpty; 95 | 96 | /** 97 | * Whether or not paging should be handled by HuskyUI 98 | * or the implementing plugin. 99 | */ 100 | private final boolean autoPaging; 101 | 102 | /** 103 | * Whether or not to center the {@link ItemStack}. 104 | * 105 | *

Currently serves no purpose.

106 | */ 107 | private final boolean centered; 108 | 109 | /** 110 | * The number of rows to appear within the {@link InventoryDimension}. 111 | * 112 | *

If {@link Page#autoPaging} is set to true, this value 113 | * will be automatically determined based off of the number of items 114 | * being digested by the Page.

115 | */ 116 | private final int rows; 117 | private boolean updatable; 118 | private int updateTickRate; 119 | private Consumer updateConsumer; 120 | private Runnable interrupt; 121 | private Inventory cachedInventory; 122 | 123 | private Task updaterTask; 124 | 125 | /** 126 | * Constructs a Page. 127 | * 128 | * @param id the ID of the State this Page extends 129 | * @param elements the {@link ItemStack}s in use by this Page 130 | * @param inventoryDimension the virtual inventory this Page represents 131 | * @param title the name of the chest 132 | * @param emptyStack the ItemStack to be used if filling blank spaces 133 | * @param updatable if the Page is updatable or not 134 | * @param updateTickRate if the page is updatable, how many ticks will be between updates. 135 | * @param updateConsumer the consumer to run if the page is updatable every {@link Page#updateTickRate} ticks. 136 | * @param fillWhenEmpty whether or not to fill blank spaces 137 | * @param autoPaging whether or not to let HuskyUI handle paging 138 | * @param centered whether or not to center ItemStacks 139 | * @param rows the number of rows within the InventoryDimension 140 | * @param parent the parent page of this page. 141 | */ 142 | public Page(@Nonnull final String id, 143 | @Nonnull final Map elements, 144 | @Nonnull final InventoryDimension inventoryDimension, 145 | @Nonnull final InventoryArchetype inventoryArchetype, 146 | @Nonnull final Text title, 147 | @Nonnull final ItemStack emptyStack, 148 | final boolean updatable, 149 | final int updateTickRate, 150 | final Consumer updateConsumer, 151 | final Runnable interrupt, 152 | final boolean fillWhenEmpty, 153 | final boolean autoPaging, 154 | final boolean centered, 155 | final int rows, 156 | final String parent) { 157 | super(id); 158 | this.elements = elements; 159 | this.inventoryDimension = inventoryDimension; 160 | this.inventoryArchetype = inventoryArchetype; 161 | this.title = title; 162 | this.emptyStack = emptyStack; 163 | this.fillWhenEmpty = fillWhenEmpty; 164 | this.autoPaging = autoPaging; 165 | this.centered = centered; 166 | this.rows = rows; 167 | this.updatable = updatable; 168 | this.updateTickRate = updateTickRate; 169 | this.updateConsumer = updateConsumer; 170 | this.interrupt = interrupt; 171 | cachedInventory = null; 172 | this.setParent(parent); 173 | } 174 | 175 | public Consumer getUpdateConsumer() { 176 | return updateConsumer; 177 | } 178 | 179 | public int getUpdateTickRate() { 180 | return updateTickRate; 181 | } 182 | 183 | public boolean isUpdatable() { 184 | return updatable; 185 | } 186 | 187 | /** 188 | * Gets the {@link ItemStack}s in use by this Page. 189 | * 190 | * @return the ItemSTacks in use by this Page 191 | */ 192 | @Nonnull 193 | public Map getElements() { 194 | return this.elements; 195 | } 196 | 197 | /** 198 | * Gets the virtual inventory this Page represents. 199 | * 200 | * @return the virtual inventory this Page represents 201 | */ 202 | @Nonnull 203 | public InventoryDimension getInventoryDimension() { 204 | return this.inventoryDimension; 205 | } 206 | 207 | /** 208 | * Gets the title of the chest this Page uses. 209 | * 210 | * @return the title of the chest 211 | */ 212 | @Nonnull 213 | public Text getTitle() { 214 | return this.title; 215 | } 216 | 217 | /** 218 | * Gets the {@link ItemStack} to use when an {@link Element} 219 | * hasn't been given. 220 | * 221 | * @return the default ItemStack 222 | */ 223 | @Nonnull 224 | public ItemStack getEmptyStack() { 225 | return this.emptyStack; 226 | } 227 | 228 | /** 229 | * Determines whether or not HuskyUI is replacing non-specified 230 | * {@link Element}s with a default {@link ItemStack}. 231 | * 232 | * @return true if filling empty Elements; false otherwise 233 | */ 234 | public boolean isFillWhenEmpty() { 235 | return this.fillWhenEmpty; 236 | } 237 | 238 | /** 239 | * Determines whether or not HuskyUI is handling paging. 240 | * 241 | * @return true if HuskyUI is handling paging; false otherwise 242 | */ 243 | public boolean isAutoPaging() { 244 | return this.autoPaging; 245 | } 246 | 247 | /** 248 | * Gets the number of rows in the {@link InventoryDimension}. 249 | * 250 | * @return the number of rows in the InventoryDimension 251 | */ 252 | public int getRows() { 253 | return this.rows; 254 | } 255 | private long ticks = 0; 256 | 257 | public long getActualTicks() { 258 | return ticks; 259 | } 260 | 261 | public long getTicks() { 262 | return (long)Math.floor(ticks/updateTickRate); 263 | } 264 | 265 | public void tickIncrement() { 266 | ticks++; 267 | } 268 | 269 | @Nonnull 270 | public Inventory getPageView() { 271 | return getPageView(0); 272 | } 273 | /** 274 | * Generates the {@link Inventory} for this Page. 275 | * 276 | * @return the Inventory for this Page 277 | */ 278 | @Nonnull 279 | public Inventory getPageView(int pagenum) { 280 | if(updatable && cachedInventory != null){ 281 | return cachedInventory; 282 | } 283 | int maxSize = Math.max(1,(this.inventoryDimension.getColumns() * (this.inventoryDimension.getRows()-1))); 284 | int pageCount = (this.elements.size() / maxSize) + 1; 285 | final Inventory inventory = Inventory.builder() 286 | .property("type", new StringProperty("huskui-page")) 287 | .property("id", new StringProperty(getId())) 288 | .property(InventoryDimension.PROPERTY_NAME, this.inventoryDimension) 289 | .of(this.inventoryArchetype) 290 | .listener(InteractInventoryEvent.class, event -> { 291 | if(event instanceof InteractInventoryEvent.Close){ 292 | if(this.interrupt != null) { 293 | interrupt(); 294 | } 295 | return; 296 | } 297 | if (!(event instanceof InteractInventoryEvent.Open) && !(event instanceof InteractInventoryEvent.Close)) { 298 | event.setCancelled(true); 299 | try{ 300 | if (event.getCursorTransaction().getDefault().getType() == ItemTypes.AIR) return; 301 | }catch (NoSuchFieldError err){ 302 | //old api support 303 | if (event.getCursorTransaction().getDefault().getType() == ItemTypes.NONE) return; 304 | } 305 | 306 | event.getCursorTransaction().getDefault().toContainer().get(DataQuery.of("UnsafeData", "slotnum")).ifPresent(slot -> { 307 | final int num = (int) slot; 308 | if (this.autoPaging) { 309 | if (num == -1) { //close/back 310 | if(hasParent()) { 311 | this.getContainer().openState(this.getObserver(), this.getParent()); 312 | }else{ 313 | InventoryUtil.close(this.getObserver()); 314 | } 315 | } else if(num == -2){ // previous page 316 | this.getContainer().openState(this.getObserver(), this.getId() + "#" + (pagenum-1)); 317 | } else if(num == -3) { // next page 318 | this.getContainer().openState(this.getObserver(), this.getId() + "#" + (pagenum+1)); 319 | }else if (this.elements.get(num) instanceof ActionableElement) { 320 | ((ActionableElement) this.elements.get(num)).getAction().runAction(this.getId(),getObserver().getOpenInventory().get()); 321 | } 322 | } else { 323 | if (this.elements.get(num) instanceof ActionableElement) { 324 | ((ActionableElement) this.elements.get(num)).getAction().runAction(this.getId(),getObserver().getOpenInventory().get()); 325 | } 326 | } 327 | }); 328 | } else { 329 | event.getCursorTransaction().setCustom(ItemStackSnapshot.NONE); 330 | event.getCursorTransaction().setValid(true); 331 | } 332 | }) 333 | .property(InventoryTitle.PROPERTY_NAME, InventoryTitle.of(this.title)) 334 | .build(HuskyUI.getInstance()); 335 | 336 | int num = 0; 337 | for (final Inventory slot : inventory.slots()) { 338 | if (this.autoPaging) { 339 | if(num == (this.rows * 9) && pageCount > 1 && pagenum > 0){ 340 | slot.set(ItemStack.builder() 341 | .fromContainer(ItemStack.builder() 342 | .itemType(ItemTypes.MAP) 343 | .add(Keys.DISPLAY_NAME, Text.of(TextStyles.RESET, TextColors.WHITE, "Previous")) 344 | .build() 345 | .toContainer() 346 | .set(DataQuery.of("UnsafeData", "slotnum"), -2)) 347 | .build()); 348 | }else if(num == (this.rows * 9) + 8 && pageCount > 1 && pagenum < pageCount-1){ 349 | slot.set(ItemStack.builder() 350 | .fromContainer(ItemStack.builder() 351 | .itemType(ItemTypes.MAP) 352 | .add(Keys.DISPLAY_NAME, Text.of(TextStyles.RESET, TextColors.WHITE, "Next")) 353 | .build() 354 | .toContainer() 355 | .set(DataQuery.of("UnsafeData", "slotnum"), -3)) 356 | .build()); 357 | }else if (num == (this.rows * 9) + 4) { 358 | slot.set(ItemStack.builder() 359 | .fromContainer(ItemStack.builder() 360 | .itemType(ItemTypes.BARRIER) 361 | .add(Keys.DISPLAY_NAME, Text.of(TextStyles.RESET, TextColors.WHITE, (hasParent())?"Back":"Close")) 362 | .build() 363 | .toContainer() 364 | .set(DataQuery.of("UnsafeData", "slotnum"), -1)) 365 | .build()); 366 | } else if (num > (this.rows * 9) - 1) { 367 | slot.set(emptyStack); 368 | } else if (this.elements.containsKey(num + (pagenum * maxSize))) { 369 | slot.set(ItemStack.builder() 370 | .fromContainer(this.elements.get(num + (pagenum * maxSize)).getItem() 371 | .toContainer() 372 | .set(DataQuery.of("UnsafeData", "slotnum"), num + (pagenum * maxSize))) 373 | .build()); 374 | } 375 | 376 | } else if (elements.containsKey(num)) { // Standard Situations 377 | slot.set(ItemStack.builder() 378 | .fromContainer(elements.get(num).getItem().toContainer() 379 | .set(DataQuery.of("UnsafeData", "slotnum"), num)) 380 | .build()); 381 | } 382 | 383 | num++; 384 | } 385 | if(updatable){ 386 | this.cachedInventory = inventory; 387 | this.updaterTask = Task.builder().intervalTicks(1).execute(() -> { 388 | if(this.getActualTicks() % this.getUpdateTickRate() == 0) { 389 | this.updateConsumer.accept(this); 390 | } 391 | this.tickIncrement(); 392 | }).submit(HuskyUI.getInstance()); 393 | } 394 | return inventory; 395 | } 396 | 397 | /** 398 | * {@inheritDoc} 399 | * 400 | *

Additionally, assigns the {@link Player} to all 401 | * {@link Element}s within this Page.

402 | * 403 | * @param observer the Player viewing this State 404 | */ 405 | @Override 406 | public void setObserver(final Player observer) { 407 | if(observer == null) { 408 | return; 409 | } 410 | 411 | super.setObserver(observer); 412 | 413 | for(final Element e : elements.values()){ 414 | if(e instanceof ActionableElement){ 415 | ((ActionableElement)e).getAction().setObserver(observer); 416 | } 417 | } 418 | } 419 | 420 | public void interrupt(){ 421 | //System.out.println("-++-\nINTERRUPT\nInterrupt Is Null: " + (this.interrupt == null) + "\nUpdater Task Is Null: " + (this.updaterTask == null)); 422 | if(hasInterupt()) { 423 | try { 424 | this.interrupt.run(); 425 | this.interrupt = null; 426 | //System.out.println("Interrupt ran."); 427 | }catch (Exception e){ 428 | HuskyUI.getLogger().error("Error occurred while running HuskyUI Page interrupt."); 429 | e.printStackTrace(); 430 | } 431 | 432 | }else{ 433 | throw new RuntimeException("Interrupt either doen't exist, or has been called already"); 434 | } 435 | if(updaterTask != null) { 436 | updaterTask.cancel(); 437 | //System.out.println("Updater cancelled"); 438 | updaterTask = null; 439 | //System.out.println("Updater set to null."); 440 | } 441 | if(hasObserver()){ 442 | getObserver().closeInventory(); 443 | } 444 | } 445 | 446 | public boolean hasInterupt() { 447 | return this.interrupt != null; 448 | } 449 | 450 | 451 | /** 452 | * {@inheritDoc} 453 | * 454 | * @param newContainer the {@link StateContainer} that will be taking 455 | * responsibility for this Page. 456 | * @return a copy of this Page 457 | */ 458 | @Nonnull 459 | @Override 460 | public Page copy(@Nonnull final StateContainer newContainer) { 461 | final PageBuilder builder = builder(); 462 | 463 | for (final Map.Entry entry : this.elements.entrySet()) { 464 | builder.putElement(entry.getKey(), entry.getValue().copy(newContainer)); 465 | } 466 | 467 | builder.setInventoryDimension(this.inventoryDimension); // InventoryDimension is Immutable. 468 | builder.setInventoryArchetype(this.inventoryArchetype); 469 | builder.setTitle(this.title); // Text is Immutable 470 | builder.setEmptyStack(this.emptyStack.copy()); 471 | builder.setFillWhenEmpty(this.fillWhenEmpty); 472 | builder.setAutoPaging(this.autoPaging); 473 | builder.setCentered(this.centered); 474 | builder.setUpdateTickRate(updateTickRate); 475 | builder.setUpdater(updateConsumer); 476 | builder.setInterrupt(interrupt); 477 | builder.setUpdatable(updatable); 478 | 479 | final Page page = builder.build(this.getId()); 480 | 481 | page.setContainer(newContainer); 482 | page.setObserver(this.getObserver()); 483 | page.setParent(this.getParent()); 484 | 485 | return page; 486 | } 487 | 488 | /** 489 | * Creates a new {@link PageBuilder}. 490 | * 491 | * @return a new PageBuilder 492 | */ 493 | public static PageBuilder builder() { 494 | return new PageBuilder(); 495 | } 496 | 497 | /** 498 | * An easy-to-use class for quickly creating {@link Page}s. 499 | */ 500 | public static class PageBuilder { 501 | 502 | /** 503 | * The {@link Element}s that will be used by this Page. 504 | * 505 | *

Elements are sorted by integer, ostensibly referring 506 | * to their placement in the chest.

507 | */ 508 | @Nonnull private final Map elements; 509 | 510 | /** 511 | * The {@link InventoryDimension} is the space in which {@link Element}s 512 | * will be placed when the inventory is opened by a {@link Player}. 513 | */ 514 | private InventoryDimension inventoryDimension; 515 | 516 | private InventoryArchetype inventoryArchetype; 517 | 518 | /** 519 | * The name of the Chest to be opened, because IDs 520 | * typically aren't very visually appealing. 521 | */ 522 | private Text title; 523 | 524 | /** 525 | * The {@link ItemStack} to be used when {@link Page#fillWhenEmpty} 526 | * is set to true. Usually this will be determined by 527 | * {@link Page#defaultEmptyStack}. 528 | */ 529 | private ItemStack emptyStack; 530 | 531 | /** 532 | * Whether or not to fill empty stacks with 533 | * predetermined {@link ItemStack}s. 534 | */ 535 | private boolean fillWhenEmpty; 536 | 537 | /** 538 | * Whether or not paging should be handled by HuskyUI 539 | * or the implementing plugin. 540 | */ 541 | private boolean autoPaging; 542 | 543 | /** 544 | * Whether or not to center the {@link ItemStack}. 545 | * 546 | *

Currently serves no purpose.

547 | */ 548 | private boolean centered; 549 | 550 | /** 551 | * Defines if the current page should be checked for updates. 552 | */ 553 | private boolean updatable; 554 | 555 | 556 | /** 557 | * How often the updater should be fired. 558 | * Defaults to a 1 tick interval. 559 | */ 560 | private int updateTickRate; 561 | 562 | private Consumer updaterConsumer; 563 | 564 | private Runnable interrupt; 565 | 566 | @Nullable 567 | private String parent; 568 | 569 | /** 570 | * Constructs a new {@link PageBuilder}, currently only 571 | * accessible via {@link Page#builder()}. 572 | */ 573 | private PageBuilder() { 574 | this.elements = Maps.newHashMap(); 575 | this.inventoryDimension = null; 576 | this.title = Text.of("Unnamed Page"); 577 | this.emptyStack = Page.defaultEmptyStack; 578 | this.fillWhenEmpty = false; 579 | this.autoPaging = false; 580 | this.centered = true; 581 | this.updatable = false; 582 | this.updateTickRate = 1; 583 | this.updaterConsumer = null; 584 | this.interrupt = null; 585 | this.parent = null; 586 | this.inventoryArchetype = InventoryArchetypes.CHEST; 587 | } 588 | 589 | /** 590 | * Puts an {@link Element} into the Page and assigns the slot 591 | * it should take up in the {@link InventoryDimension}. 592 | * 593 | * @param slot the position in the InventoryDimension 594 | * @param element the {@link Element} to be assigned 595 | * @return this PageBuilder 596 | */ 597 | public PageBuilder putElement(final int slot, @Nonnull final Element element) { 598 | this.elements.put(slot, element); 599 | return this; 600 | } 601 | 602 | /** 603 | * Adds an {@link Element} to the Page. The Element is 604 | * automatically assigned after the last one. 605 | * 606 | * @param element the Element to be added 607 | * @return this PageBuilder 608 | */ 609 | public PageBuilder addElement(@Nonnull final Element element) { 610 | this.elements.put(this.elements.size(), element); 611 | return this; 612 | } 613 | 614 | /** 615 | * Sets the {@link InventoryDimension} to be used by this Page. 616 | * 617 | * @param inventoryDimension the InventoryDimension to be used 618 | * @return this PageBuilder 619 | */ 620 | @Nonnull 621 | public PageBuilder setInventoryDimension(@Nonnull final InventoryDimension inventoryDimension) { 622 | this.inventoryDimension = inventoryDimension; 623 | return this; 624 | } 625 | 626 | @Nonnull 627 | public PageBuilder setInventoryArchetype(@Nonnull final InventoryArchetype inventoryArchetype){ 628 | this.inventoryArchetype = inventoryArchetype; 629 | return this; 630 | } 631 | 632 | /** 633 | * Sets the title of this chest 634 | * 635 | * @param title the title of the chest 636 | * @return this PageBuilder 637 | */ 638 | @Nonnull 639 | public PageBuilder setTitle(@Nonnull final Text title) { 640 | this.title = title; 641 | return this; 642 | } 643 | 644 | public PageBuilder setParent(@Nullable String parent) { 645 | this.parent = parent; 646 | return this; 647 | } 648 | 649 | /** 650 | * Sets the {@link ItemStack} to be used as a default, if 651 | * no {@link Element} is assigned. 652 | * 653 | *

If unset, this will default to {@link Page#defaultEmptyStack}.

654 | * 655 | * @param emptyStack An ItemStack that will act as the defaultEmptyStack. 656 | * @return this PageBuilder 657 | */ 658 | @Nonnull 659 | public PageBuilder setEmptyStack(@Nonnull final ItemStack emptyStack) { 660 | this.emptyStack = emptyStack; 661 | return this; 662 | } 663 | 664 | /** 665 | * Sets whether or not to fill empty spaces. 666 | * 667 | * @param fillWhenEmpty whether or not to fill empty spaces 668 | * @return this PageBuilder 669 | */ 670 | @Nonnull 671 | public PageBuilder setFillWhenEmpty(final boolean fillWhenEmpty) { 672 | this.fillWhenEmpty = fillWhenEmpty; 673 | return this; 674 | } 675 | 676 | /** 677 | * Sets whether or not to let HuskyUI to handling paging. 678 | * 679 | * @param autoPaging whether or not HuskyUI should handle paging 680 | * @return this PageBuilder 681 | */ 682 | @Nonnull 683 | public PageBuilder setAutoPaging(final boolean autoPaging) { 684 | this.autoPaging = autoPaging; 685 | return this; 686 | } 687 | 688 | /** 689 | * Sets whether or not {@link ItemStack}s should be centered. 690 | * 691 | * @param centered whether or not ItemStacks should be centered 692 | * @return this PageBuilder 693 | */ 694 | @Nonnull 695 | public PageBuilder setCentered(final boolean centered) { 696 | this.centered = centered; 697 | return this; 698 | } 699 | 700 | @Nonnull 701 | public PageBuilder setUpdatable(final boolean updatable){ 702 | this.updatable = updatable; 703 | return this; 704 | } 705 | 706 | @Nonnull 707 | public PageBuilder setUpdateTickRate(final int updateTickRate){ 708 | this.updateTickRate = updateTickRate; 709 | return this; 710 | } 711 | 712 | @Nonnull 713 | public PageBuilder setUpdater(final Consumer updaterConsumer){ 714 | this.updaterConsumer = updaterConsumer; 715 | return this; 716 | } 717 | 718 | @Nonnull 719 | public PageBuilder setInterrupt(final Runnable interrupt){ 720 | this.interrupt = interrupt; 721 | return this; 722 | } 723 | 724 | /** 725 | * Builds this PageBuilder to get a new {@link Page} object. 726 | * 727 | * @param id the ID of the new State (Page) 728 | * @return a new Page 729 | */ 730 | public Page build(@Nonnull final String id) { 731 | final int rows = (this.inventoryDimension == null) ? Math.min(5,(int) Math.ceil(((double) this.elements.size()) / 9d)) : this.inventoryDimension.getRows()-1; 732 | InventoryDimension real = (this.inventoryDimension == null ? 733 | (this.autoPaging) ? 734 | InventoryDimension.of(9, rows + 1) : 735 | InventoryDimension.of(9,4) 736 | : this.inventoryDimension); 737 | return new Page(id, 738 | this.elements, 739 | real, 740 | this.inventoryArchetype, 741 | this.title, 742 | this.emptyStack, 743 | this.updatable, 744 | this.updateTickRate, 745 | this.updaterConsumer, 746 | this.interrupt, 747 | this.fillWhenEmpty, 748 | this.autoPaging, 749 | this.centered, 750 | rows, 751 | parent 752 | ); 753 | } 754 | } 755 | } 756 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | Preamble 9 | 10 | The GNU General Public License is a free, copyleft license for 11 | software and other kinds of works. 12 | 13 | The licenses for most software and other practical works are designed 14 | to take away your freedom to share and change the works. By contrast, 15 | the GNU General Public License is intended to guarantee your freedom to 16 | share and change all versions of a program--to make sure it remains free 17 | software for all its users. We, the Free Software Foundation, use the 18 | GNU General Public License for most of our software; it applies also to 19 | any other work released this way by its authors. You can apply it to 20 | your programs, too. 21 | 22 | When we speak of free software, we are referring to freedom, not 23 | price. Our General Public Licenses are designed to make sure that you 24 | have the freedom to distribute copies of free software (and charge for 25 | them if you wish), that you receive source code or can get it if you 26 | want it, that you can change the software or use pieces of it in new 27 | free programs, and that you know you can do these things. 28 | 29 | To protect your rights, we need to prevent others from denying you 30 | these rights or asking you to surrender the rights. Therefore, you have 31 | certain responsibilities if you distribute copies of the software, or if 32 | you modify it: responsibilities to respect the freedom of others. 33 | 34 | For example, if you distribute copies of such a program, whether 35 | gratis or for a fee, you must pass on to the recipients the same 36 | freedoms that you received. You must make sure that they, too, receive 37 | or can get the source code. And you must show them these terms so they 38 | know their rights. 39 | 40 | Developers that use the GNU GPL protect your rights with two steps: 41 | (1) assert copyright on the software, and (2) offer you this License 42 | giving you legal permission to copy, distribute and/or modify it. 43 | 44 | For the developers' and authors' protection, the GPL clearly explains 45 | that there is no warranty for this free software. For both users' and 46 | authors' sake, the GPL requires that modified versions be marked as 47 | changed, so that their problems will not be attributed erroneously to 48 | authors of previous versions. 49 | 50 | Some devices are designed to deny users access to install or run 51 | modified versions of the software inside them, although the manufacturer 52 | can do so. This is fundamentally incompatible with the aim of 53 | protecting users' freedom to change the software. The systematic 54 | pattern of such abuse occurs in the area of products for individuals to 55 | use, which is precisely where it is most unacceptable. Therefore, we 56 | have designed this version of the GPL to prohibit the practice for those 57 | products. If such problems arise substantially in other domains, we 58 | stand ready to extend this provision to those domains in future versions 59 | of the GPL, as needed to protect the freedom of users. 60 | 61 | Finally, every program is threatened constantly by software patents. 62 | States should not allow patents to restrict development and use of 63 | software on general-purpose computers, but in those that do, we wish to 64 | avoid the special danger that patents applied to a free program could 65 | make it effectively proprietary. To prevent this, the GPL assures that 66 | patents cannot be used to render the program non-free. 67 | 68 | The precise terms and conditions for copying, distribution and 69 | modification follow. 70 | 71 | TERMS AND CONDITIONS 72 | 73 | 0. Definitions. 74 | 75 | "This License" refers to version 3 of the GNU General Public License. 76 | 77 | "Copyright" also means copyright-like laws that apply to other kinds of 78 | works, such as semiconductor masks. 79 | 80 | "The Program" refers to any copyrightable work licensed under this 81 | License. Each licensee is addressed as "you". "Licensees" and 82 | "recipients" may be individuals or organizations. 83 | 84 | To "modify" a work means to copy from or adapt all or part of the work 85 | in a fashion requiring copyright permission, other than the making of an 86 | exact copy. The resulting work is called a "modified version" of the 87 | earlier work or a work "based on" the earlier work. 88 | 89 | A "covered work" means either the unmodified Program or a work based 90 | on the Program. 91 | 92 | To "propagate" a work means to do anything with it that, without 93 | permission, would make you directly or secondarily liable for 94 | infringement under applicable copyright law, except executing it on a 95 | computer or modifying a private copy. Propagation includes copying, 96 | distribution (with or without modification), making available to the 97 | public, and in some countries other activities as well. 98 | 99 | To "convey" a work means any kind of propagation that enables other 100 | parties to make or receive copies. Mere interaction with a user through 101 | a computer network, with no transfer of a copy, is not conveying. 102 | 103 | An interactive user interface displays "Appropriate Legal Notices" 104 | to the extent that it includes a convenient and prominently visible 105 | feature that (1) displays an appropriate copyright notice, and (2) 106 | tells the user that there is no warranty for the work (except to the 107 | extent that warranties are provided), that licensees may convey the 108 | work under this License, and how to view a copy of this License. If 109 | the interface presents a list of user commands or options, such as a 110 | menu, a prominent item in the list meets this criterion. 111 | 112 | 1. Source Code. 113 | 114 | The "source code" for a work means the preferred form of the work 115 | for making modifications to it. "Object code" means any non-source 116 | form of a work. 117 | 118 | A "Standard Interface" means an interface that either is an official 119 | standard defined by a recognized standards body, or, in the case of 120 | interfaces specified for a particular programming language, one that 121 | is widely used among developers working in that language. 122 | 123 | The "System Libraries" of an executable work include anything, other 124 | than the work as a whole, that (a) is included in the normal form of 125 | packaging a Major Component, but which is not part of that Major 126 | Component, and (b) serves only to enable use of the work with that 127 | Major Component, or to implement a Standard Interface for which an 128 | implementation is available to the public in source code form. A 129 | "Major Component", in this context, means a major essential component 130 | (kernel, window system, and so on) of the specific operating system 131 | (if any) on which the executable work runs, or a compiler used to 132 | produce the work, or an object code interpreter used to run it. 133 | 134 | The "Corresponding Source" for a work in object code form means all 135 | the source code needed to generate, install, and (for an executable 136 | work) run the object code and to modify the work, including scripts to 137 | control those activities. However, it does not include the work's 138 | System Libraries, or general-purpose tools or generally available free 139 | programs which are used unmodified in performing those activities but 140 | which are not part of the work. For example, Corresponding Source 141 | includes interface definition files associated with source files for 142 | the work, and the source code for shared libraries and dynamically 143 | linked subprograms that the work is specifically designed to require, 144 | such as by intimate data communication or control flow between those 145 | subprograms and other parts of the work. 146 | 147 | The Corresponding Source need not include anything that users 148 | can regenerate automatically from other parts of the Corresponding 149 | Source. 150 | 151 | The Corresponding Source for a work in source code form is that 152 | same work. 153 | 154 | 2. Basic Permissions. 155 | 156 | All rights granted under this License are granted for the term of 157 | copyright on the Program, and are irrevocable provided the stated 158 | conditions are met. This License explicitly affirms your unlimited 159 | permission to run the unmodified Program. The output from running a 160 | covered work is covered by this License only if the output, given its 161 | content, constitutes a covered work. This License acknowledges your 162 | rights of fair use or other equivalent, as provided by copyright law. 163 | 164 | You may make, run and propagate covered works that you do not 165 | convey, without conditions so long as your license otherwise remains 166 | in force. You may convey covered works to others for the sole purpose 167 | of having them make modifications exclusively for you, or provide you 168 | with facilities for running those works, provided that you comply with 169 | the terms of this License in conveying all material for which you do 170 | not control copyright. Those thus making or running the covered works 171 | for you must do so exclusively on your behalf, under your direction 172 | and control, on terms that prohibit them from making any copies of 173 | your copyrighted material outside their relationship with you. 174 | 175 | Conveying under any other circumstances is permitted solely under 176 | the conditions stated below. Sublicensing is not allowed; section 10 177 | makes it unnecessary. 178 | 179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law. 180 | 181 | No covered work shall be deemed part of an effective technological 182 | measure under any applicable law fulfilling obligations under article 183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or 184 | similar laws prohibiting or restricting circumvention of such 185 | measures. 186 | 187 | When you convey a covered work, you waive any legal power to forbid 188 | circumvention of technological measures to the extent such circumvention 189 | is effected by exercising rights under this License with respect to 190 | the covered work, and you disclaim any intention to limit operation or 191 | modification of the work as a means of enforcing, against the work's 192 | users, your or third parties' legal rights to forbid circumvention of 193 | technological measures. 194 | 195 | 4. Conveying Verbatim Copies. 196 | 197 | You may convey verbatim copies of the Program's source code as you 198 | receive it, in any medium, provided that you conspicuously and 199 | appropriately publish on each copy an appropriate copyright notice; 200 | keep intact all notices stating that this License and any 201 | non-permissive terms added in accord with section 7 apply to the code; 202 | keep intact all notices of the absence of any warranty; and give all 203 | recipients a copy of this License along with the Program. 204 | 205 | You may charge any price or no price for each copy that you convey, 206 | and you may offer support or warranty protection for a fee. 207 | 208 | 5. Conveying Modified Source Versions. 209 | 210 | You may convey a work based on the Program, or the modifications to 211 | produce it from the Program, in the form of source code under the 212 | terms of section 4, provided that you also meet all of these conditions: 213 | 214 | a) The work must carry prominent notices stating that you modified 215 | it, and giving a relevant date. 216 | 217 | b) The work must carry prominent notices stating that it is 218 | released under this License and any conditions added under section 219 | 7. This requirement modifies the requirement in section 4 to 220 | "keep intact all notices". 221 | 222 | c) You must license the entire work, as a whole, under this 223 | License to anyone who comes into possession of a copy. This 224 | License will therefore apply, along with any applicable section 7 225 | additional terms, to the whole of the work, and all its parts, 226 | regardless of how they are packaged. This License gives no 227 | permission to license the work in any other way, but it does not 228 | invalidate such permission if you have separately received it. 229 | 230 | d) If the work has interactive user interfaces, each must display 231 | Appropriate Legal Notices; however, if the Program has interactive 232 | interfaces that do not display Appropriate Legal Notices, your 233 | work need not make them do so. 234 | 235 | A compilation of a covered work with other separate and independent 236 | works, which are not by their nature extensions of the covered work, 237 | and which are not combined with it such as to form a larger program, 238 | in or on a volume of a storage or distribution medium, is called an 239 | "aggregate" if the compilation and its resulting copyright are not 240 | used to limit the access or legal rights of the compilation's users 241 | beyond what the individual works permit. Inclusion of a covered work 242 | in an aggregate does not cause this License to apply to the other 243 | parts of the aggregate. 244 | 245 | 6. Conveying Non-Source Forms. 246 | 247 | You may convey a covered work in object code form under the terms 248 | of sections 4 and 5, provided that you also convey the 249 | machine-readable Corresponding Source under the terms of this License, 250 | in one of these ways: 251 | 252 | a) Convey the object code in, or embodied in, a physical product 253 | (including a physical distribution medium), accompanied by the 254 | Corresponding Source fixed on a durable physical medium 255 | customarily used for software interchange. 256 | 257 | b) Convey the object code in, or embodied in, a physical product 258 | (including a physical distribution medium), accompanied by a 259 | written offer, valid for at least three years and valid for as 260 | long as you offer spare parts or customer support for that product 261 | model, to give anyone who possesses the object code either (1) a 262 | copy of the Corresponding Source for all the software in the 263 | product that is covered by this License, on a durable physical 264 | medium customarily used for software interchange, for a price no 265 | more than your reasonable cost of physically performing this 266 | conveying of source, or (2) access to copy the 267 | Corresponding Source from a network server at no charge. 268 | 269 | c) Convey individual copies of the object code with a copy of the 270 | written offer to provide the Corresponding Source. This 271 | alternative is allowed only occasionally and noncommercially, and 272 | only if you received the object code with such an offer, in accord 273 | with subsection 6b. 274 | 275 | d) Convey the object code by offering access from a designated 276 | place (gratis or for a charge), and offer equivalent access to the 277 | Corresponding Source in the same way through the same place at no 278 | further charge. You need not require recipients to copy the 279 | Corresponding Source along with the object code. If the place to 280 | copy the object code is a network server, the Corresponding Source 281 | may be on a different server (operated by you or a third party) 282 | that supports equivalent copying facilities, provided you maintain 283 | clear directions next to the object code saying where to find the 284 | Corresponding Source. Regardless of what server hosts the 285 | Corresponding Source, you remain obligated to ensure that it is 286 | available for as long as needed to satisfy these requirements. 287 | 288 | e) Convey the object code using peer-to-peer transmission, provided 289 | you inform other peers where the object code and Corresponding 290 | Source of the work are being offered to the general public at no 291 | charge under subsection 6d. 292 | 293 | A separable portion of the object code, whose source code is excluded 294 | from the Corresponding Source as a System Library, need not be 295 | included in conveying the object code work. 296 | 297 | A "User Product" is either (1) a "consumer product", which means any 298 | tangible personal property which is normally used for personal, family, 299 | or household purposes, or (2) anything designed or sold for incorporation 300 | into a dwelling. In determining whether a product is a consumer product, 301 | doubtful cases shall be resolved in favor of coverage. For a particular 302 | product received by a particular user, "normally used" refers to a 303 | typical or common use of that class of product, regardless of the status 304 | of the particular user or of the way in which the particular user 305 | actually uses, or expects or is expected to use, the product. A product 306 | is a consumer product regardless of whether the product has substantial 307 | commercial, industrial or non-consumer uses, unless such uses represent 308 | the only significant mode of use of the product. 309 | 310 | "Installation Information" for a User Product means any methods, 311 | procedures, authorization keys, or other information required to install 312 | and execute modified versions of a covered work in that User Product from 313 | a modified version of its Corresponding Source. The information must 314 | suffice to ensure that the continued functioning of the modified object 315 | code is in no case prevented or interfered with solely because 316 | modification has been made. 317 | 318 | If you convey an object code work under this section in, or with, or 319 | specifically for use in, a User Product, and the conveying occurs as 320 | part of a transaction in which the right of possession and use of the 321 | User Product is transferred to the recipient in perpetuity or for a 322 | fixed term (regardless of how the transaction is characterized), the 323 | Corresponding Source conveyed under this section must be accompanied 324 | by the Installation Information. But this requirement does not apply 325 | if neither you nor any third party retains the ability to install 326 | modified object code on the User Product (for example, the work has 327 | been installed in ROM). 328 | 329 | The requirement to provide Installation Information does not include a 330 | requirement to continue to provide support service, warranty, or updates 331 | for a work that has been modified or installed by the recipient, or for 332 | the User Product in which it has been modified or installed. Access to a 333 | network may be denied when the modification itself materially and 334 | adversely affects the operation of the network or violates the rules and 335 | protocols for communication across the network. 336 | 337 | Corresponding Source conveyed, and Installation Information provided, 338 | in accord with this section must be in a format that is publicly 339 | documented (and with an implementation available to the public in 340 | source code form), and must require no special password or key for 341 | unpacking, reading or copying. 342 | 343 | 7. Additional Terms. 344 | 345 | "Additional permissions" are terms that supplement the terms of this 346 | License by making exceptions from one or more of its conditions. 347 | Additional permissions that are applicable to the entire Program shall 348 | be treated as though they were included in this License, to the extent 349 | that they are valid under applicable law. If additional permissions 350 | apply only to part of the Program, that part may be used separately 351 | under those permissions, but the entire Program remains governed by 352 | this License without regard to the additional permissions. 353 | 354 | When you convey a copy of a covered work, you may at your option 355 | remove any additional permissions from that copy, or from any part of 356 | it. (Additional permissions may be written to require their own 357 | removal in certain cases when you modify the work.) You may place 358 | additional permissions on material, added by you to a covered work, 359 | for which you have or can give appropriate copyright permission. 360 | 361 | Notwithstanding any other provision of this License, for material you 362 | add to a covered work, you may (if authorized by the copyright holders of 363 | that material) supplement the terms of this License with terms: 364 | 365 | a) Disclaiming warranty or limiting liability differently from the 366 | terms of sections 15 and 16 of this License; or 367 | 368 | b) Requiring preservation of specified reasonable legal notices or 369 | author attributions in that material or in the Appropriate Legal 370 | Notices displayed by works containing it; or 371 | 372 | c) Prohibiting misrepresentation of the origin of that material, or 373 | requiring that modified versions of such material be marked in 374 | reasonable ways as different from the original version; or 375 | 376 | d) Limiting the use for publicity purposes of names of licensors or 377 | authors of the material; or 378 | 379 | e) Declining to grant rights under trademark law for use of some 380 | trade names, trademarks, or service marks; or 381 | 382 | f) Requiring indemnification of licensors and authors of that 383 | material by anyone who conveys the material (or modified versions of 384 | it) with contractual assumptions of liability to the recipient, for 385 | any liability that these contractual assumptions directly impose on 386 | those licensors and authors. 387 | 388 | All other non-permissive additional terms are considered "further 389 | restrictions" within the meaning of section 10. If the Program as you 390 | received it, or any part of it, contains a notice stating that it is 391 | governed by this License along with a term that is a further 392 | restriction, you may remove that term. If a license document contains 393 | a further restriction but permits relicensing or conveying under this 394 | License, you may add to a covered work material governed by the terms 395 | of that license document, provided that the further restriction does 396 | not survive such relicensing or conveying. 397 | 398 | If you add terms to a covered work in accord with this section, you 399 | must place, in the relevant source files, a statement of the 400 | additional terms that apply to those files, or a notice indicating 401 | where to find the applicable terms. 402 | 403 | Additional terms, permissive or non-permissive, may be stated in the 404 | form of a separately written license, or stated as exceptions; 405 | the above requirements apply either way. 406 | 407 | 8. Termination. 408 | 409 | You may not propagate or modify a covered work except as expressly 410 | provided under this License. Any attempt otherwise to propagate or 411 | modify it is void, and will automatically terminate your rights under 412 | this License (including any patent licenses granted under the third 413 | paragraph of section 11). 414 | 415 | However, if you cease all violation of this License, then your 416 | license from a particular copyright holder is reinstated (a) 417 | provisionally, unless and until the copyright holder explicitly and 418 | finally terminates your license, and (b) permanently, if the copyright 419 | holder fails to notify you of the violation by some reasonable means 420 | prior to 60 days after the cessation. 421 | 422 | Moreover, your license from a particular copyright holder is 423 | reinstated permanently if the copyright holder notifies you of the 424 | violation by some reasonable means, this is the first time you have 425 | received notice of violation of this License (for any work) from that 426 | copyright holder, and you cure the violation prior to 30 days after 427 | your receipt of the notice. 428 | 429 | Termination of your rights under this section does not terminate the 430 | licenses of parties who have received copies or rights from you under 431 | this License. If your rights have been terminated and not permanently 432 | reinstated, you do not qualify to receive new licenses for the same 433 | material under section 10. 434 | 435 | 9. Acceptance Not Required for Having Copies. 436 | 437 | You are not required to accept this License in order to receive or 438 | run a copy of the Program. Ancillary propagation of a covered work 439 | occurring solely as a consequence of using peer-to-peer transmission 440 | to receive a copy likewise does not require acceptance. However, 441 | nothing other than this License grants you permission to propagate or 442 | modify any covered work. These actions infringe copyright if you do 443 | not accept this License. Therefore, by modifying or propagating a 444 | covered work, you indicate your acceptance of this License to do so. 445 | 446 | 10. Automatic Licensing of Downstream Recipients. 447 | 448 | Each time you convey a covered work, the recipient automatically 449 | receives a license from the original licensors, to run, modify and 450 | propagate that work, subject to this License. You are not responsible 451 | for enforcing compliance by third parties with this License. 452 | 453 | An "entity transaction" is a transaction transferring control of an 454 | organization, or substantially all assets of one, or subdividing an 455 | organization, or merging organizations. If propagation of a covered 456 | work results from an entity transaction, each party to that 457 | transaction who receives a copy of the work also receives whatever 458 | licenses to the work the party's predecessor in interest had or could 459 | give under the previous paragraph, plus a right to possession of the 460 | Corresponding Source of the work from the predecessor in interest, if 461 | the predecessor has it or can get it with reasonable efforts. 462 | 463 | You may not impose any further restrictions on the exercise of the 464 | rights granted or affirmed under this License. For example, you may 465 | not impose a license fee, royalty, or other charge for exercise of 466 | rights granted under this License, and you may not initiate litigation 467 | (including a cross-claim or counterclaim in a lawsuit) alleging that 468 | any patent claim is infringed by making, using, selling, offering for 469 | sale, or importing the Program or any portion of it. 470 | 471 | 11. Patents. 472 | 473 | A "contributor" is a copyright holder who authorizes use under this 474 | License of the Program or a work on which the Program is based. The 475 | work thus licensed is called the contributor's "contributor version". 476 | 477 | A contributor's "essential patent claims" are all patent claims 478 | owned or controlled by the contributor, whether already acquired or 479 | hereafter acquired, that would be infringed by some manner, permitted 480 | by this License, of making, using, or selling its contributor version, 481 | but do not include claims that would be infringed only as a 482 | consequence of further modification of the contributor version. For 483 | purposes of this definition, "control" includes the right to grant 484 | patent sublicenses in a manner consistent with the requirements of 485 | this License. 486 | 487 | Each contributor grants you a non-exclusive, worldwide, royalty-free 488 | patent license under the contributor's essential patent claims, to 489 | make, use, sell, offer for sale, import and otherwise run, modify and 490 | propagate the contents of its contributor version. 491 | 492 | In the following three paragraphs, a "patent license" is any express 493 | agreement or commitment, however denominated, not to enforce a patent 494 | (such as an express permission to practice a patent or covenant not to 495 | sue for patent infringement). To "grant" such a patent license to a 496 | party means to make such an agreement or commitment not to enforce a 497 | patent against the party. 498 | 499 | If you convey a covered work, knowingly relying on a patent license, 500 | and the Corresponding Source of the work is not available for anyone 501 | to copy, free of charge and under the terms of this License, through a 502 | publicly available network server or other readily accessible means, 503 | then you must either (1) cause the Corresponding Source to be so 504 | available, or (2) arrange to deprive yourself of the benefit of the 505 | patent license for this particular work, or (3) arrange, in a manner 506 | consistent with the requirements of this License, to extend the patent 507 | license to downstream recipients. "Knowingly relying" means you have 508 | actual knowledge that, but for the patent license, your conveying the 509 | covered work in a country, or your recipient's use of the covered work 510 | in a country, would infringe one or more identifiable patents in that 511 | country that you have reason to believe are valid. 512 | 513 | If, pursuant to or in connection with a single transaction or 514 | arrangement, you convey, or propagate by procuring conveyance of, a 515 | covered work, and grant a patent license to some of the parties 516 | receiving the covered work authorizing them to use, propagate, modify 517 | or convey a specific copy of the covered work, then the patent license 518 | you grant is automatically extended to all recipients of the covered 519 | work and works based on it. 520 | 521 | A patent license is "discriminatory" if it does not include within 522 | the scope of its coverage, prohibits the exercise of, or is 523 | conditioned on the non-exercise of one or more of the rights that are 524 | specifically granted under this License. You may not convey a covered 525 | work if you are a party to an arrangement with a third party that is 526 | in the business of distributing software, under which you make payment 527 | to the third party based on the extent of your activity of conveying 528 | the work, and under which the third party grants, to any of the 529 | parties who would receive the covered work from you, a discriminatory 530 | patent license (a) in connection with copies of the covered work 531 | conveyed by you (or copies made from those copies), or (b) primarily 532 | for and in connection with specific products or compilations that 533 | contain the covered work, unless you entered into that arrangement, 534 | or that patent license was granted, prior to 28 March 2007. 535 | 536 | Nothing in this License shall be construed as excluding or limiting 537 | any implied license or other defenses to infringement that may 538 | otherwise be available to you under applicable patent law. 539 | 540 | 12. No Surrender of Others' Freedom. 541 | 542 | If conditions are imposed on you (whether by court order, agreement or 543 | otherwise) that contradict the conditions of this License, they do not 544 | excuse you from the conditions of this License. If you cannot convey a 545 | covered work so as to satisfy simultaneously your obligations under this 546 | License and any other pertinent obligations, then as a consequence you may 547 | not convey it at all. For example, if you agree to terms that obligate you 548 | to collect a royalty for further conveying from those to whom you convey 549 | the Program, the only way you could satisfy both those terms and this 550 | License would be to refrain entirely from conveying the Program. 551 | 552 | 13. Use with the GNU Affero General Public License. 553 | 554 | Notwithstanding any other provision of this License, you have 555 | permission to link or combine any covered work with a work licensed 556 | under version 3 of the GNU Affero General Public License into a single 557 | combined work, and to convey the resulting work. The terms of this 558 | License will continue to apply to the part which is the covered work, 559 | but the special requirements of the GNU Affero General Public License, 560 | section 13, concerning interaction through a network will apply to the 561 | combination as such. 562 | 563 | 14. Revised Versions of this License. 564 | 565 | The Free Software Foundation may publish revised and/or new versions of 566 | the GNU General Public License from time to time. Such new versions will 567 | be similar in spirit to the present version, but may differ in detail to 568 | address new problems or concerns. 569 | 570 | Each version is given a distinguishing version number. If the 571 | Program specifies that a certain numbered version of the GNU General 572 | Public License "or any later version" applies to it, you have the 573 | option of following the terms and conditions either of that numbered 574 | version or of any later version published by the Free Software 575 | Foundation. If the Program does not specify a version number of the 576 | GNU General Public License, you may choose any version ever published 577 | by the Free Software Foundation. 578 | 579 | If the Program specifies that a proxy can decide which future 580 | versions of the GNU General Public License can be used, that proxy's 581 | public statement of acceptance of a version permanently authorizes you 582 | to choose that version for the Program. 583 | 584 | Later license versions may give you additional or different 585 | permissions. However, no additional obligations are imposed on any 586 | author or copyright holder as a result of your choosing to follow a 587 | later version. 588 | 589 | 15. Disclaimer of Warranty. 590 | 591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY 592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT 593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY 594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, 595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM 597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF 598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 599 | 600 | 16. Limitation of Liability. 601 | 602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE 606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF 607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD 608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), 609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF 610 | SUCH DAMAGES. 611 | 612 | 17. Interpretation of Sections 15 and 16. 613 | 614 | If the disclaimer of warranty and limitation of liability provided 615 | above cannot be given local legal effect according to their terms, 616 | reviewing courts shall apply local law that most closely approximates 617 | an absolute waiver of all civil liability in connection with the 618 | Program, unless a warranty or assumption of liability accompanies a 619 | copy of the Program in return for a fee. 620 | 621 | END OF TERMS AND CONDITIONS 622 | 623 | How to Apply These Terms to Your New Programs 624 | 625 | If you develop a new program, and you want it to be of the greatest 626 | possible use to the public, the best way to achieve this is to make it 627 | free software which everyone can redistribute and change under these terms. 628 | 629 | To do so, attach the following notices to the program. It is safest 630 | to attach them to the start of each source file to most effectively 631 | state the exclusion of warranty; and each file should have at least 632 | the "copyright" line and a pointer to where the full notice is found. 633 | 634 | {one line to give the program's name and a brief idea of what it does.} 635 | Copyright (C) {year} {name of author} 636 | 637 | This program is free software: you can redistribute it and/or modify 638 | it under the terms of the GNU General Public License as published by 639 | the Free Software Foundation, either version 3 of the License, or 640 | (at your option) any later version. 641 | 642 | This program is distributed in the hope that it will be useful, 643 | but WITHOUT ANY WARRANTY; without even the implied warranty of 644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 645 | GNU General Public License for more details. 646 | 647 | You should have received a copy of the GNU General Public License 648 | along with this program. If not, see . 649 | 650 | Also add information on how to contact you by electronic and paper mail. 651 | 652 | If the program does terminal interaction, make it output a short 653 | notice like this when it starts in an interactive mode: 654 | 655 | {project} Copyright (C) {year} {fullname} 656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 657 | This is free software, and you are welcome to redistribute it 658 | under certain conditions; type `show c' for details. 659 | 660 | The hypothetical commands `show w' and `show c' should show the appropriate 661 | parts of the General Public License. Of course, your program's commands 662 | might be different; for a GUI interface, you would use an "about box". 663 | 664 | You should also get your employer (if you work as a programmer) or school, 665 | if any, to sign a "copyright disclaimer" for the program, if necessary. 666 | For more information on this, and how to apply and follow the GNU GPL, see 667 | . 668 | 669 | The GNU General Public License does not permit incorporating your program 670 | into proprietary programs. If your program is a subroutine library, you 671 | may consider it more useful to permit linking proprietary applications with 672 | the library. If this is what you want to do, use the GNU Lesser General 673 | Public License instead of this License. But first, please read 674 | . 675 | --------------------------------------------------------------------------------