InventoryClickEvent gets fired multiple times - java

I have an abstract GUI parent class handling a click event.
public abstract class GUI implements Listener {
private final Inventory inventory;
public GUI(Player player) {
Bukkit.getPluginManager().registerEvents(this, NPCs.getPlugin());
ItemStack fillerItem = new ItemStack(getFiller());
ItemMeta fillerItemMeta = fillerItem.getItemMeta();
fillerItemMeta.setDisplayName("");
fillerItem.setItemMeta(fillerItemMeta);
int inventorySize = (getFunctionalItems().size()>=54) ? 54 : getFunctionalItems().size()+(9-getFunctionalItems().size()%9)*Math.min(1, getFunctionalItems().size()%9);
inventory = Bukkit.createInventory(player, inventorySize, getName());
for(int i = 0; i < inventory.getSize(); i++) {
inventory.setItem(i, fillerItem);
}
for(int i = 0; i < getFunctionalItems().size(); i++) {
inventory.setItem(i, getFunctionalItems().get(i));
}
}
#EventHandler
public void onClick(InventoryClickEvent event) {
handle(event);
}
public abstract ArrayList<ItemStack> getFunctionalItems();
public abstract String getName();
protected abstract void handle(InventoryClickEvent event);
public Material getFiller() {
return Material.GRAY_STAINED_GLASS_PANE;
}
public Inventory getInventory() {
return inventory;
}
protected final ItemStack createFunctionalItem(String name, Material material) {
ItemStack itemStack = new ItemStack(material);
ItemMeta itemMeta = itemStack.getItemMeta();
itemMeta.setDisplayName(name);
itemStack.setItemMeta(itemMeta);
return itemStack;
}
}
In my child class it gets handled like following
#Override
public void handle(InventoryClickEvent event) {
ItemStack clicked = event.getCurrentItem();
Player player = (Player) event.getWhoClicked();
MainGUI mainGUI = new MainGUI(player);
NameGUI nameGUI = new NameGUI(player);
SkinGUI skinGUI = new SkinGUI(player);
//Main GUI
if(Arrays.equals(event.getClickedInventory().getContents(), mainGUI.getInventory().getContents())) {
switch(clicked.getItemMeta().getDisplayName()) {
case "Set Name" -> player.openInventory(nameGUI.getInventory());
case "Set Skin" -> player.openInventory(skinGUI.getInventory());
}
event.setCancelled(true);
}
}
But if I test it gets called 2 times on the first click and on the following so many times it even forces my game to crash. I know I can just put in a delay but I really want to know why this is the case.
Thank you

It's running multiple times because you are adding each new GUI to listener.
For example, here:
MainGUI mainGUI = new MainGUI(player);
NameGUI nameGUI = new NameGUI(player);
SkinGUI skinGUI = new SkinGUI(player);
You are creating 3 GUIs, so 3 new inventory click event register in listener.
To fix this, I suggest you to :
Make ONE class that receive InventoryClickEvent event, and call GUI that you be called.
For example, you have a list with all GUI:
public static List<GUI> ALL_GUIS = new ArrayList<>();
Then, you should have a way to determine in which inventory the player clicked, such as:
Inventory name. Not very good solution if this can change, but easier to create and use
Inventory holder like this:
public class MainGUIHolder implements InventoryHolder {
#Override
public Inventory getInventory() {
return null;
}
}
Now use it like that:
Inventory mainInv = Bukkit.createInventory(new MainGUIHolder(), 9, "My inv");
You can check with inventory instanceof MainGUIHolder, then get the holder instance with maybe some object that is currently edited by the player
Don't create multiple times GUI instance. For me, it's not a good way, and I think it's better to do like this :
MainGUI mainGUI = GuiManager.getMainGUID(); // here get the alone main GUI instance
// now use it

Related

Java JTabbedPane or how to get the custom Object that created the Tab

I am often struggling with the same problem of custom Objects that creates a e.g. gui Component. And I never know what is the best way to get from the gui Component back to the object.
So multiple hacks and tricks are welcome.
Let me explain it to you:
This is my custom Object I need to find afterwards
public class MyObject {
int yearOfBirth;
String name;
public MyObject(int yearOfBirth, String name) {
this.yearOfBirth = yearOfBirth;
this.name = name;
}
public int getYearOfBirth() {
return yearOfBirth;
}
public Component getPanel() {
Component panel1 = makeTextPanel("This is the personal tab of "+name);
return panell;
}
}
This is where I need to find it through the Tab I am focusing
public class MyTabControl implements ChangeListener {
JTabbedPane myTabPane = new JTabbedPane();
public MyTabControl(){
//This will add a Listener for clicking on one Tab
myTabPane.addChangeListener(this);
}
public void oneMoreTab(MyObject myObject) {
myTabPane.addTab(myObject.name, myObject.getPanel())
}
public void stateChanged(ChangeEvent arg0) {
System.out.println("Focus of Tab changed");
int actualFocusedTabIndex = myTabPane.getSelectedIndex();
Component acutalFocusedComponent = myTabPane.getComponentAt(actualFocusedTabIndex);
//This works fine, I can get the Tab. Or at least the Component.
//But how do I get the yearOfBirth or better the Object itself?
int yearOfBirthOfTheSelectedTab = ???
}
}
This is just the main function
public static void main(String[] args) {
//Commands to start and create the GUI
MyTabControl myTabControl = new MyTabControl();
MyObject mother = new MyObject(1960, "Helen");
myTabControl.oneMoreTab(mother);
MyObject father = new MyObject(1955, "James");
myTabControl.oneMoreTab(father);
}
EDIT:
1 not working solution: Extend Component class
I have tried to extend the class Component. But this will create a failure (see comment in code):
public class ComponentWithExtras extends Component {
MyObject myObject;
public void addMyObject(MyObject myObject) {
this.myObject = myObject;
}
}
// The following line will create failure: Can't cast Component to ComponentWithExtras
ComponentWithExtras componentWithExtras = (ComponentWithExtras) myObject.getPanel();
componentWithExtras.addMyObject(myObject);
myTabPane.addTab(myObject.name, componentWithExtras);

How to get the name of an item for an event (minecraft plugin)

I've managed to code everything and debug everything on my own which is an accomplishment, well everything except one thing..
I need help figuring out how to check the name of an item and if the item has that specific name, THEN the event will happen. I'm trying to check if a chicken egg has a specific mob name, like creeper, or spider, and if so, that mob will spawn out of the egg. (I know they have mob eggs for that but I'm messing around with my abilities atm.)
Here's the code for the command:
public class EggCommands implements CommandExecutor {
#Override
public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) {
Player player = (Player) sender;
if (cmd.getName().equalsIgnoreCase("egg") && sender instanceof Player) {
if (args.length == 0) {
player.sendMessage("Usage: /egg <mob>");
return true;
}
if (args[0].equals("creeper")) {
if (player.getInventory().getSize() == -1) {
player.sendMessage(ChatColor.DARK_RED + "You're inventory is full");
return true;
} else {
ItemStack item = new ItemStack(Material.EGG, 1);
ItemMeta meta = item.getItemMeta();
meta.setDisplayName(ChatColor.DARK_GREEN + "Creeper");
ArrayList<String> lore = new ArrayList<String>();
lore.add(ChatColor.RED + "Spawns A Charged Creeper With Lightning Effect");
meta.setLore(lore);
item.setItemMeta(meta);
player.getInventory().addItem(item);
return true;
}
}
}
return true;
}
}
Here's the code for the Event Listener:
public class EggListener implements Listener {
public EggListener(EggMain plugin) {
plugin.getServer().getPluginManager().registerEvents(this, plugin);
}
#EventHandler
public void Eggs(PlayerEggThrowEvent e) {
if (e.getEgg().getCustomName().equals(ChatColor.DARK_GREEN + "Creeper")) {
e.setHatchingType(EntityType.CREEPER);
e.setNumHatches((byte) 1);
e.setHatching(true);
}
}
}
Reason why your code won't work is that you're comparing two different names. You set the name of an ItemStack to "Creeper", but then in the PlayerEggThrowEvent you check if the Egg's name is the same as the ItemStack, it is not.
An egg is an entity, its name is the same as a nametag over a zombie's or a player's head, not the same as an ItemStack. The egg will infact have no name unless you set one.
I experimented a while and came up with this structure, it's not working but might give you a head start:
private HashMap<UUID, ItemStack> eggThrown = new HashMap<UUID, ItemStack>();
#EventHandler
public void eggthrown(PlayerEggThrowEvent event) {
if(eggThrown.containsKey(event.getPlayer().getUniqueId())) {
ItemStack egg = eggThrown.get(event.getPlayer().getUniqueId());
}
}
#EventHandler(priority = EventPriority.HIGHEST)
public void storeEgg(PlayerInteractEvent event) {
if(event.getAction().equals(Action.RIGHT_CLICK_AIR) || event.getAction().equals(Action.RIGHT_CLICK_BLOCK)) {
if(event.hasItem() && event.getItem().getType().equals(Material.EGG)) {
eggThrown.put(event.getPlayer().getUniqueId(), event.getItem());
}
}
}
What I do is use PlayerInteractEvent to retrieve the egg's ItemStack, this event is called only when a player right clicks (throws an egg) thanks to the if statements. I then put the ItemStack into a hashmap we can use in the PlayerEggThrowEvent. When we have the ItemStack we can compare the names.
Another solution would be cancel whatever event that gets called when the egg is spawned into the world and give it a custom name.

Waiting for Sub Tasks to Finish in JavaFX

I am trying to create a simple game using JavaFX. The game consists in a main game and sub-games, which the player may have to play, depending on the result of the main game. In the end, the main game has to update it's state (p.e.: score) depending on the sub-games result.
I made a simplified and generalized version of how I have implemented the game logic:
Result play(Player p) {
Result r = p.play(this);
for(SubGame game : r.getSubGames())
{
p.play(game);
}
update(r);
return r;
}
This game works perfectly in the terminal, since it has a linear execution.
But using JavaFX (implemented in the Player), I cannot control the flow of the program properly, because of the game loop.
I have followed this tutorial to handle multiple screens for the main game and sub-games. The Player class can successfully change the screen to a sub-game, using the handler. But the update no longer waits for the sub-games to be played, and this function returns while the player is mid-game.
I am trying to keep the game logic separate from UI, so changes to the code showed above shouldn't be dependent on the interface framework.
Any help?
Use an event-driven approach, setting the values of observable properties, and responding when they change.
For example, you might encapsulate the state of the game with
public class GameState {
private ObservableList<SubGame> currentGames = FXCollections.observableArrayList();
public ObservableList<SubGame> getCurrentGames() {
return currentGames();
}
private ReadOnlyObjectWrapper<SubGame> currentGame = new ReadOnlyObjectProperty<>();
public ReadOnlyObjectProperty<SubGame> currentGameProperty() {
return currentGame.getReadOnlyProperty() ;
}
public final SubGame getCurrentGame() {
return currentGameProperty().get();
}
public GameState() {
// initialize sub game list...
}
public void nextGame() {
int index = currentGames.indexOf(currentGame.get());
if (index < currentGames.size() - 1) {
currentGame.set(currentGames.get(index + 1));
}
}
public void start() {
currentGame.set(currentGames().get(0));
}
public boolean hasMoreGames() {
return currentGames.indexOf(currentGame.get()) < currentGames.size() - 1 ;
}
}
And similarly you might have some observable state in your SubGame class:
public class SubGame {
private final BooleanProperty finished = new SimpleBooleanProperty();
public BooleanProperty finishedProperty() {
return finished ;
}
public final boolean isFinished() {
return finishedProperty().get();
}
public final void setFinished(boolean finished) {
finishedProperty().set(finished) ;
}
// ...
}
Now your game logic is just implemented with listeners:
void play(Player p) {
Result r = p.play(this);
GameState gameState = new GameState();
gameState.currentGameProperty().addListener((obs, oldGame, newGame) -> {
newGame.finishedProperty().addListener((obs, wasFinished, isNowFinished) -> {
if (isNowFinished) {
// maybe update score etc based on state of newGame...
if (gameState.hasMoreGames()) {
gameState.nextGame();
} else {
// logic here for "all games are finished...
}
}
});
});
gameState.start();
}
Obviously the details of how you implement this depend on your requirements etc, but this general approach should work for anything you need.

Cannot get variable from Child back to Parent in JAVA (Options Window)

STARTED - 3:00PM
UPDATE 1 - 5:36PM
Apply Button in the Option() class:
private void cmdApplyActionPerformed(java.awt.event.ActionEvent evt) {
// TODO add your handling code here:
hud.setTime(btnTxtTime);
hud.setTemp(btnTxtTemp);
hud.setSurface(btnTxtSurface);
hud.setWeather(btnTxtWeather);
hud.setRadiation(btnTxtRadiation);
dispose();
}
This is a section of the Option() Class.
public class Options extends javax.swing.JFrame {
public String btnTxtTime;
public String btnTxtTemp;
public String btnTxtSurface;
public String btnTxtWeather;
public String btnTxtRadiation;
public static boolean ApplyClicked;
/**
* Creates new form Profile
*/
private HUD hud;
public Options(HUD hud) {
initComponents();
this.hud = hud;
}
This is a method in Option() class:
public String getTime() {
if ("Day".equals(grpTimeOfDay.getSelection())) {
btnTxtTime = "Day";
return this.btnTxtTime;
}
if ("Night".equals(grpTimeOfDay.getSelection())) {
btnTxtTime = "Night";
return this.btnTxtTime;
}
return null;
}
This is how Options() is openned from within HUD():
private void cmdOptionsActionPerformed(java.awt.event.ActionEvent evt) {
// TODO add your handling code here:
Options o = new Options(hud);
this.getLocation(p);
o.setLocation((int) p.getX() + 100, (int) p.getY() + 100);
o.setVisible(true);
}
This is the start of my HUD() Class:
public abstract class HUD extends javax.swing.JFrame implements Runnable {
private Options o;
private HUD hud;
public HUD(Options o) {
initComponents();
this.o = o;
and this is the method from HUD() which gets the value of the JButtons from Options():
public void setTime(String strTime) {
strTime = o.getTime();
txtTime.setText(strTime);
}
However whenever I click Apply, the options set in Options() are not then set in the TextFields that display them in HUD() like they should be :/
It's difficult to navigate through your very lengthy code sample, however take a look at your cmdApplyActionPerformed() method. You are creating a new HUD() and setting values in it... and then doing absolutely nothing with it.
If you are trying to use the "Apply" button to modify an existing HUD object, your class needs to have a reference to it somewhere. If the HUD is the parent class which creates the Options, try having the Options store a reference to the parent in its constructor. Then, when you perform changes like this in the Options, you can perform them on the parent rather than on a new variable which has no effect.
private HUD parent;
/**
* Creates new form Profile
*/
public Options(HUD parent) {
initComponents();
this.parent = parent;
}
Then, in your event handler, you can have ...
parent.setTime(btnTxtTime);
parent.setTemp(btnTxtTemp);
parent.setSurface(btnTxtSurface);
parent.setWeather(btnTxtWeather);
parent.setRadiation(btnTxtRadiation);
dispose();
From what I understand, HUD is your 'main window' and the users gets to this option frame from that window.
But when you apply, you're setting the properties on a new HUD, not the one you had before.
To fix this, you need a handle to your main window in your config window, so that you can set the properties on it.
in your hud:
ConfigFrame config = new ConfigFrame();
config.setHUD(this);
config.setVisible(true);
In your config
private HUD hud;
public void setHUD(HUD hud){
this.hud = hud;
}
then just leave out the HUD hud = new hud();

Java access a public variable outside a class, SecurityException: MIDlet not constructed by createMIDlet

I'm a newbie in java and I have a small problem. I want to access a variable in one class from another. I have three classes and I want to be able to access a variable in the main class to enable me read the array.
The error I am getting is
java.lang.SecurityException: MIDlet not constructed by createMIDlet
Please see the example below. Please bear in mind they're all in the same package.
package tungPackage;
import com.sun.lwuit.*;
import com.sun.lwuit.animations.CommonTransitions;
import com.sun.lwuit.events.ActionEvent;
import com.sun.lwuit.events.ActionListener;
import javax.microedition.midlet.MIDlet;
public class TungMidlet extends MIDlet implements ActionListener {
private Command back = new Command("Back");
private Command ok = new Command("Ok");
public ActionListener commandlistListener = new ActionListener() {
public void actionPerformed(ActionEvent cmd) {
// check which command cliked
if (cmd.getCommand() == back) {
// go back to previous form
mainForm.show();
} else if (cmd.getCommand() == ok) {
// go forward
}
}
};
private List list;
private Form mainForm;
private Label promptLabel;
private housesClass houseClassObject = new housesClass();
public int counter; //this is the variable I want to access in a class called calculate class object.
private int sumAmmt;
public TungMidlet tungMidletObject;
public calculateClass calculateClassObject;
public TungMidlet() {
Display.init(this);
}
private ActionListener applistListener = new ActionListener() {
public void actionPerformed(ActionEvent ae) {
if(list.getSelectedIndex()==0){
counter++;
if (counter>5)
{
//check sum price.
sumAmmt = calculateClassObject.calculateSum();
Dialog x = new Dialog("info");
Label label = new Label("Maximum reached.");
Label label2 = new Label("Sum ammt = "+sumAmmt);
x.addComponent(label);
x.addComponent(label2);
x.addCommand(ok);
x.show();
}
else
{
//calculate the price
String info = houseClassObject.randomHouse();
Dialog x = new Dialog("info");
Label label = new Label(info);
x.addComponent(label);
x.addCommand(ok);
x.show();
}
}
}
};
public void startApp() {
//calculateClassObject = new calculateClass();
//sumAmmt = calculateClassObject.calculate(sumAmmt);
mainForm = new Form("Investment Categories");
promptLabel = new Label("choose category");
list = new List();
list.addItem("House");
list.addItem("Cars");
list.addItem("Schools");
list.addItem("Schools");
list.addItem("Supermarkets");
list.addItem("Stocks");
list.addItem("Land");
list.addActionListener(applistListener);
mainForm.addComponent(promptLabel);
mainForm.addComponent(list);
mainForm.addCommand(back);
mainForm.addCommandListener(commandlistListener);
mainForm.setTransitionInAnimator(CommonTransitions.createSlide(CommonTransitions.SLIDE_HORIZONTAL, true, 1000));
mainForm.show();
}
public void pauseApp() {}
public void destroyApp(boolean unconditional) {}
public void actionPerformed(ActionEvent ae) {
throw new UnsupportedOperationException("Not supported yet.");
}
}
The class I want to access the "counter" variable using is shown below.
package tungPackage;
import java.util.Random;
public class housesClass {
public Random generator = new Random();
public String[] houseArray = new String[5];
public housesClass housesClassObject;
public calculateClass calcobj;// = new calculateClass();
public housesClass()
{
}
public String randomHouse() {
housesClassObject = new housesClass();
houseArray[0] = "Bungalow - 20,000,000 Shillings";
houseArray[1] = "Microhouse - 10,000,000 Shillings";
houseArray[2] = "Flat - 200,000,000 shillings";
houseArray[3] = "Garage apartment - 7,000,000 shillings";
houseArray[4] = "Studio apartment - 13,000,000 shillings";
int rnd = generator.nextInt(houseArray.length);
housesClassObject.housePrices(rnd);///noma
String house = houseArray[rnd];
return house;
}
void housePrices(int houseNumber) {
calcobj = new calculateClass();
TungMidlet tungmidobj = new TungMidlet();
int counter = tungmidobj.counter;
int[] housePriceArray = new int[5];
housePriceArray[0] = 20000000;
housePriceArray[1] = 10000000;
housePriceArray[2] = 200000000;
housePriceArray[3] = 7000000;
housePriceArray[4] = 13000000;
int price = housePriceArray[houseNumber];
calcobj.storePrice(counter,price);
}
}
The other supporting class is shown below.
package tungPackage;
public class calculateClass {
int[] storeArray = new int[5];
public calculateClass()
{
}
public void storePrice(int counter, int number2)
{
storeArray[counter] = number2;
}
public int calculateSum()
{
int sum =0;
for(int i=1; i<6; i++){
sum= sum+storeArray[i];
}
return sum;
}
}
Are you getting an error? It looks like your access code should work.
I can't seem to find anywhere that you actually initialise counter though, so maybe your problem is that you need to put counter = 0; somewhere in your code.
Java is also object oriented so you should avoid accessing like the above and make some 'getter and setter' methods:
public int getCounter() {
return counter;
}
and then call int counter = tungmidobj.getCounter();
remove TungMidlet constructor. If there was something useful to do there, you could also declare it protected - but this is not the case with your code snippet, see below.
Wherever you try to invoke that constructor directly, remove code that does this and find another way to do what you need. If needed, study code examples provided in LWUIT Tutorial - Introduction for how typical things are done in LWUIT.
put statement Display.init() in the beginning of the startApp method,
just like it is done in LWUIT Tutorial - Hello, LWUIT! example code
The reason why you are getting SecurityException is because you invoke TungMidlet constructor directly. Don't do that.
MIDP API documentation for MIDlet constructor states:
Throws:
SecurityException - unless the application management software is creating the MIDlet.
one way is
TungMidlet tungMidlet=new TungMidlet();
System.out.println(tungMidlet.counter);
but know encapsulation
second way is
you can make counter private variable and provide setter and getters.
private int counter;
public void setCounter(int counter){
this.counter=counter;
}
public int getCounter(){
return counter;
}
second way is preferred way as it achieves encapsulation

Categories

Resources