Waiting for Sub Tasks to Finish in JavaFX - java

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.

Related

How could I make a force field effect on a player?

I want to make a player Invunerable to damage and all knockback for a certain period of time (except for fall damage). My rpoblem is that the bukkit runable is not wroking as intended:
public class WeaponEvents implements Listener {
private Elemental_weapons plugin;
ArrayList<UUID> forceFieldArray = new ArrayList<UUID>();
#EventHandler
public void onplayercrouchevent(PlayerToggleSneakEvent event) {
boolean issneaking = event.isSneaking();
Player player = event.getPlayer();
if (issneaking==true) {
if (player.getInventory().getChestplate().equals(Weapons.chestplate)) {
forceFieldArray.add(player.getUniqueId());
Bukkit.broadcastMessage(String.valueOf(forceFieldArray));
new BukkitRunnable() {
#Override
public void run() {
Bukkit.broadcastMessage("no more");
forceFieldArray.remove(player.getUniqueId());
}
}.runTaskLater( plugin, 200L);
while (forceFieldArray.contains(player.getUniqueId())) {
}
}
}
}
I'd do this by having an array, say
ArrayList<UUID> forceFieldArray = new ArrayList<UUID>();
then whenever you want to activate your player's forcefield use a BukkitRunnable to set a delayed task.
forceFieldArray.add(player.getUniqueId());
new BukkitRunnable() {
#Override
public void run() {
forceFieldArray.remove(player.getUniqueId());
}
}.runTaskLater(plugin, 200L);
Just remember that the '200L' in there is ticks, 20 ticks to a second so 200L is 10 seconds. This adds the player to the array, then in ten seconds it'll remove the player. Using this you can now use that array to check if players have the force field active.
public void onPlayerDamage(EntityDamageEvent e) {
//Check if entity is player
//Create player variable
if(e.getCause() == DamageCause./*[Damage type you want to cancel]*/) {
if(forceFieldArray.contains(player.getUniqueId())) {
e.setCancelled(true);
}
}
}
This way you could even make a config with a list in there, have all the types of damage you want the forcefield to block then just above the ifs bring that data into an Array and say if(configArray.contains(e.getCause()) { //Do stuff
Hope this helped!

How to add a item to player

I'm trying to add some custom ability to this plugin by using it own API. I have some trouble adding items to players when they got a kill. I have tried a lot of different methods to add potion to the killer by using .addItem(). There is no wiki for this plugin and on their page, there is some information about the API. Here is my code
public class ChemistryAbility extends Ability{
//Return name of ability
#Override
public String getName() {
return "Chemistry";
}
//Get the data form config file.
#Override
public void load(FileConfiguration file) {
// TODO Auto-generated method stub
}
//Get the activate Material
#Override
public Material getActivationMaterial() {
return null;
}
//Get the activate projectile
#Override
public EntityType getActivationProjectile() {
return null;
}
//Will this ability activate when player attack another player
#Override
public boolean isAttackActivated() {
return true;
}
//Will this ability activate when player get attacked by another player
#Override
public boolean isAttackReceiveActivated() {
return false;
}
//Will this ability activate when player get damage
#Override
public boolean isDamageActivated() {
return false;
}
//Will this ability activate when player interact with another player
#Override
public boolean isEntityInteractionActivated() {
return false;
}
#Override
public boolean execute(final Player p, final PlayerData PD, final Event e) {
ItemStack potion = new ItemStack(Material.SPLASH_POTION, 3);
PotionMeta pmeta = (PotionMeta) potion.getItemMeta();
pmeta.addCustomEffect(new PotionEffect(PotionEffectType.HARM, 1, 2), true);
potion.setItemMeta(pmeta);
Player killer = p.getKiller();
if (p.isDead()) {
p.getKiller();
if (p.getKiller() instanceof Player) {
killer.getInventory().addItem(potion);
}
}
return false;
}
}
K, So i don't know that much about plugin dev, but here is my best shot with 15 min of research. Not sure about the API, but if u can implement this outside the event, try this
#EventHandler
public void onKill(PlayerDeathEvent e) {
if (e.getEntity().getKiller() != null) { // to check if there was actually a killer
Entity killer = e.getEntity.getKiller(); // stores killer instance
if (killer.hasMetadata("Chemistry")) { // checks if has class
killer.getInventory().addItem(new ItemStack(Material.BOOK));
}
}
}
i think. i honestly dont know
Potions have always been a pain in Minecraft. Mojang changes how item data works.
I have some Deprecated code that I made. This should work for a few of the upcoming updates.
public ItemStack getPotionItem(Color c, PotionEffectType type, PotionType pType, boolean splash, int time, int level) {
Potion p = new Potion(pType);
p.setSplash(splash);
p.setType(pType);
ItemStack pot = new ItemStack(Material.SPLASH_POTION);
PotionMeta im = (PotionMeta) pot.getItemMeta();
List<String> lores = new ArrayList<String>();
lores.add(getLore());
im.setDisplayName(getName());
im.addCustomEffect(new PotionEffect(type, time, level - 1), true);
im.setLore(lores);
im.setColor(getColor());
im.addItemFlags(ItemFlag.HIDE_POTION_EFFECTS);
im.addItemFlags(ItemFlag.HIDE_ATTRIBUTES);
pot.setItemMeta(im);
p.apply(pot);
return pot;
}

Java8 - "effectively final"

I'm using RxVertx which is a sort of RxJava along with Java8 and I have a compilation error.
Here is my code:
public rx.Observable<Game> findGame(long templateId, GameModelType game_model, GameStateType state) {
return context.findGame(templateId, state)
.flatMap(new Func1<RxMessage<byte[]>, rx.Observable<Game>>() {
#Override
public Observable<Game> call(RxMessage<byte[]> gameRawReply) {
Game game = null;
switch(game_model) {
case SINGLE: {
ebs.subscribe(new Action1<RxMessage<byte[]>>() {
#Override
public void call(RxMessage<byte[]> t1) {
if(!singleGame.contains(0) {
game = new Game(); // ERROR is at this line
singleGames.put(0, game);
} else {
game = singleGames.get(0); // ERROR is at this line
}
}
});
}
}
return rx.Observable.from(game);
}
});
}
The compilation error is:
"Local variable game defined in an enclosing scope must be final or effectively final"
I cannot define 'game' as final since I do allocation\set and return it at the end of the function.
How can I make this code compile??
Thanks.
I have a Holder class that I use for situations like this.
/**
* Make a final one of these to hold non-final things in.
*
* #param <T>
*/
public class Holder<T> {
private T held = null;
public Holder() {
}
public Holder(T it) {
held = it;
}
public void hold(T it) {
held = it;
}
public T held() {
return held;
}
public boolean isEmpty() {
return held == null;
}
#Override
public String toString() {
return String.valueOf(held);
}
}
You can then do stuff like:
final Holder<Game> theGame = new Holder<>();
...
theGame.hold(myGame);
...
{
// Access the game through the `final Holder`
theGame.held() ....
Since you need to not modify the reference of the object you can wrap the Game in something else.
The quickest (but ugly) fix is to use an array of size 1, then set the content of the array later. This works because the the array is effectively final, what is contained in the array doesn't have to be.
#Override
public Observable<Game> call(RxMessage<byte[]> gameRawReply) {
Game[] game = new Game[1];
switch(game_model) {
case SINGLE: {
ebs.subscribe(new Action1<RxMessage<byte[]>>() {
#Override
public void call(RxMessage<byte[]> t1) {
if(!singleGame.contains(0) {
game[0] = new Game();
singleGames.put(0, game[0]);
} else {
game[0] = singleGames.get(0);
}
}
});
}
}
return rx.Observable.from(game[0]);
}
Another similar option is to make a new class that has a Game field and you then set that field later.
Cyclops has Mutable, and LazyImmutable objects for handling this use case. Mutable is fully mutable, and LazyImmutable is set once.
Mutable<Game> game = Mutable.of(null);
public void call(RxMessage<byte[]> t1) {
if(!singleGame.contains(0) {
game.mutate(g -> new Game());
singleGames.put(0, game.get());
} else {
game[0] = game.mutate(g->singleGames.get(0));
}
}
LazyImmutable can be used to set a value, lazily, once :
LazyImmutable<Game> game = LazyImmutable.def();
public void call(RxMessage<byte[]> t1) {
//new Game() is only ever called once
Game g = game.computeIfAbsent(()->new Game());
}
You cant. At least not directly. U can use a wrapper class however: just define a class "GameContainer" with game as its property and foward a final reference to this container instead.
#dkatzel's suggestion is a good one, but there's another option: extract everything about retrieving/creating the Game into a helper method, and then declare final Game game = getOrCreateGame();. I think that's cleaner than the final array approach, though the final array approach will certainly work.
Although the other approaches look acceptable, I'd like to mention that you can't be sure subscribing to ebs will be synchronous and you may end up always returning null from the inner function. Since you depend on another Observable, you could just simply compose it through:
public rx.Observable<Game> findGame(
long templateId,
GameModelType game_model,
GameStateType state) {
return context.findGame(templateId, state)
.flatMap(gameRawReply -> {
switch(game_model) {
case SINGLE: {
return ebs.map(t1 -> {
Game game;
if (!singleGame.contains(0) {
game = new Game();
singleGames.put(0, game);
} else {
game = singleGames.get(0);
}
return game;
});
}
}
return rx.Observable.just(null);
});
}

An FSM with object state in Java

I'm implementing a Finite State Machine in java classes. I wasn't able to find another example with the states and events being their own objects (perhaps there's a reason to that?), and I'm not quite convinced with my solution, especially because I have to assign a state to the FSM (the actual manager) and then assign the FSM to the state (to notify a state change). Here's the code for the state manager, to which I assign a state, and spits it out if asked to:
public class FSM {
public void setCurrentState(FSMState newCurrentState) {
this.currentState = newCurrentState;
}
private FSMState currentState;
public FSMState getCurrentState() {
if (this.currentState == null)
System.out.println("No current state");
return this.currentState;
}
}
and here's the state, that uses a map to map event to output state, and in case of a transition, notifies the FSM class:
public class FSMState implements EventListener {
private FSM managingFSM;
private Map<Event,FSMState> transitions;
public FSMState(FSM managingFSM) {
this.transitions = new HashMap<Event, FSMState>();
this.managingFSM = managingFSM;
}
public void addEventTransition(Event event, FSMState outputState){
transitions.put(event, outputState);
event.registerListener(this);
}
#Override
public void eventOccured(Event e) {
FSMState newState = transitions.get(e);
this.managingFSM.setCurrentState(newState);
}
}
Is there a way, with a kind of similar solution, so that the state wouldn't have to notify the manager?
I'd say your implementation needs quite a few changes. Firstly, you probably want to create the states and their transitions before create the FSM itself. I would make a separate class to manage the initialisation and setup called FSMManager.
First change FSMState to be simply this:
public class FSMState {
private Map<Event,FSMState> transitions;
public FSMState() {
this.transitions = new HashMap<Event, FSMState>();
}
public void addEventTransition(Event event, FSMState outputState){
transitions.put(event, outputState);
}
public FSMState transition(Event e) {
if(transitions.containsKey(e)) {
return transitions.get(e);
}
else {
System.out.println("No transition found");
return this;
}
}
}
Then change the FSM class to look like this:
public class FSM implements EventListener {
private FSMState currentState;
public FSM(FSMState startState) {
currentState = startState;
}
public FSMState getCurrentState() {
if (this.currentState == null)
System.out.println("No current state");
return this.currentState;
}
#Override
public void eventOccured(Event e) {
currentState = currentState.transition(e);
}
}
Finally add the manager to initialise everything:
public class FSMManager {
private static final NUM_STATES = 5;
public static void main(String[] args) {
FSMState[] states = new FSMState[NUM_STATES];
for(FSMState state : states) {
state = new FSMState();
}
// Then add all the state transitions to all the states
states[0].addEventTransition(event1, states[1]);
states[1].addEventTransition(event1, states[2]);
states[1].addEventTransition(event2, states[4]);
// etc, etc
//Finally, create the FSM
FSM fsm = new FSM(state[0]);
//You will also have to register the FSM to listen for all the events
event1.registerListener(fsm);
event2.registerListener(fsm);
...
}
}
I don't know where you are getting all the Events from and how they are actually fired, but the above pattern is a good one to follow. This way when an event is fired, it will only be applied to the current state. The same events will produce different transitions in different states, as they would in a state machine.
This solution should help however it is not perfect as I do not know exactly how you are doing this, so if you have any questions/further problems comment and I will try to improve my answer.

java events and listeners, bad implementation?

I am currently implementing custom events and listeners according to the code posted below. I have been told that this is a very dirty implementation and that this needs to be changed. However, i am very new to java and android and do not see what is wrong with the current implementation. The way i have it below works and seems to be doing everything i needed it too. I was wondering if some people could please take a look at my code and make some suggestions on what i should change and what i am doing wrong. Taking my example and modifying it so that i can see what your talking about would be greatly appreciated.
Thanks in advance!
/* SmartApp.java */
public class SmartApp extends Activity
{
private ConnectDevice cD = new ConnectDevice();
private DataRobot dR = new DataRobot();
private DataBuilder dB = new DataBuilder();
private DataSender dS = new DataSender();
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.intro);
cD.addDataReceivedListener(new DataReceivedListener() {
#Override
public void dataReceivedReceived(DataReceivedEvent event) {
// TODO Auto-generated method stub
dR.analyzeData(event.getData());
}
});
dR.addDataAnalyzedListener(new DataAnalyzedListener() {
#Override
public void dataAnalyzedReceived(DataAnalyzedEvent event) {
// TODO Auto-generated method stub
dB.submitData(event.getData());
}
});
dB.addDataBuilderListener(new DataBuilderListener() {
#Override
public void dataBuilderReceived(DataBuilderEvent event) {
// TODO Auto-generated method stub
dS.sendData(event.getData());
}
});
}
}
/* ConnectDevice.java
* This class is implementing runnable because i have a thread running that is checking
* the contents of a socket. Irrelevant to events. */
public class ConnectDevice implements Runnable {
private List _listeners = new ArrayList();
private String data;
/* Constructor */
public ConnectDevice() {// does some socket stuff here, irrelevant to the events}
public void run() {// does some socket stuff here, irrelevant to the events}
public synchronized void addDataReceivedListener(DataReceivedListener listener) {
_listeners.add(listener);
}
public synchronized void removeDataReceivedListener(DataReceivedListener listener) {
_listeners.remove(listener);
}
private synchronized void fireDataReceivedEvent(String temp) {
DataReceivedEvent dRE = new DataReceivedEvent(this, temp);
Iterator listeners = _listeners.iterator();
while(listeners.hasNext()) {
((DataReceivedListener)listeners.next()).dataReceivedReceived(dRE);
}
}
public interface DataReceivedListener {
public void dataReceivedReceived(DataReceivedEvent event);
}
}
/* DataRobot.java */
public class DataRobot {
/* This class is for analyzing the data */
private List _listeners = new ArrayList();
private String data;
public boolean analyzeData(String temp) {
/* Analyze the data
* This function analyzes the data, as explained in the OP
* This function fires the analyzed data event when finished
* analyzing the data.
*/
data = temp;
fireDataAnalyzedEvent(data); // this fires the dataanalyzedevent
return true; //for now this will always return true
}
public synchronized void addDataAnalyzedListener(DataAnalyzedListener listener) {
_listeners.add(listener);
}
public synchronized void removeDataAnalyzedListener(DataAnalyzedListener listener) {
_listeners.remove(listener);
}
private synchronized void fireDataAnalyzedEvent(String temp) {
DataAnalyzedEvent dRE = new DataAnalyzedEvent(this, temp);
Iterator listeners = _listeners.iterator();
while(listeners.hasNext()) {
((DataAnalyzedListener)listeners.next()).dataAnalyzedReceived(dRE);
}
}
public interface DataAnalyzedListener {
public void dataAnalyzedReceived(DataAnalyzedEvent event);
}
}
/* DataBuilder.java */
public class DataBuilder {
private List _listeners = new ArrayList();
private String data;
public boolean submitData(String temp) {
/* Builds the data
* This function builds the data, as explained in the OP
* This function fires the databuilder data event when finished
* building the data.
*/
data = temp;
fireDataBuilderEvent(data); //firing the databuilder event when finished
return true;
}
public synchronized void addDataBuilderListener(DataBuilderListener listener) {
_listeners.add(listener);
}
public synchronized void removeDataBuilderListener(DataBuilderListener listener) {
_listeners.remove(listener);
}
private synchronized void fireDataBuilderEvent(String temp) {
DataBuilderEvent dRE = new DataBuilderEvent(this, temp);
Iterator listeners = _listeners.iterator();
while(listeners.hasNext()) {
((DataBuilderListener)listeners.next()).dataBuilderReceived(dRE);
}
}
public interface DataBuilderListener {
public void dataBuilderReceived(DataBuilderEvent event);
}
}
/* DataSender.java */
/* this class has no event, because it is done firing events at this point */
public class DataSender {
private String data;
public boolean sendData(String temp) {
data = temp;
return true;
}
}
Below here are the event objects for each event. I Have each of this defined in a separate file, not sure if that is good procedure or not.
/* DataReceivedEvent.java */
public class DataReceivedEvent extends EventObject{
private String data;
public DataReceivedEvent(Object source, String temp) {
super(source);
// TODO Auto-generated constructor stub
data = temp;
}
public String getData() {
// this function is just an accessor function
return data;
}
}
/* DataAnalyzedEvent.java */
public class DataAnalyzedEvent extends EventObject{
private String data;
public DataAnalyzedEvent(Object source, String temp) {
super(source);
// TODO Auto-generated constructor stub
data = temp;
}
public String getData() {
// this function is just an accessor function
return data;
}
}
/* DataBuilderEvent.java */
public class DataBuilderEvent extends EventObject {
private String data;
public DataBuilderEvent(Object source, String temp) {
super(source);
// TODO Auto-generated constructor stub
data = temp;
}
public String getData() {
// this function is just an accessor function
return data;
}
}
I would not say it is a "very dirty implementation". Using callbacks/observers/listeners is a good practice in my opinion.
When I write Android applications I like to layer it such that the "application" is plain old Java with no Android imports and could theoretically be used in a Swing app, a Java EE-based web site, etc. The "Android" part is strictly user interface.
What I use callbacks for is to allow the Android code to register interest in events that take place in the application. For example, in a Blackjack game, an Activity might call game.getDealer().playHand() to tell the application to perform the dealer hand play logic. As that logic executes in the application, events are fired like cardDrawn(card), cardFlipped(card), handTotalChanged(handTotal), etc. The Android part of the app listens to these and redraws things on the screen accordingly (but it knows nothing about Blackjack).
I actually just have my activities implement interfaces like CardListener, HandListener, etc. so they can receive the event directly (unlike how you do it), but your style isn't necessarily a bad way.
I agree with #SingleShot in theory, for the parts of your Android application that can be Android-agnostic, and so long as the overhead introduced by all the indirection layers does not slow the app down too much. IMHO, in many apps, there is relatively little that fits this description.
In another post, you proposed your above solution for one activity to communicate to another activity. In Android, activities aren't just some Java objects you can toss around willy-nilly. They are managed by the OS and have particular lifecycles. While the observer/observable pattern is quite delightful in some places, it is unsuitable where the observer/observable connection will create garbage collection problems. In particular, one activity cannot, and should not, be trying to hold some sort of listener interface on another activity.
Similarly, a clean observer/observable pattern may break down in the face of databases, threads, services, and other bits of Android reality.
So, in pure Java code, isolated from Android, what you have is probably OK. However, do not go around recommending it as solutions for Android-specific problems unless you know it will work for those Android-specific problems. And, when you start trying to make your code work in an Android app, please do not be shocked if you run into problems trying to make your textbook pattern implementation work within the constraints placed upon Android apps.

Categories

Resources