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;
}
Related
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!
I am currently working on a Bukkit plugin for Minecraft and I am getting a java.lang.InstantiationError error while trying to create a new object. The stacktrace is as follows;
Caused by: java.lang.InstantiationError: fancyui.craterhater.components.FancyComponent
at fancyui.craterhater.uibuilder.ComponentMaker.getComponent(ComponentMaker.java:13) ~[?:?]
at fancyui.craterhater.uibuilder.Editor_Page_0.getComponents(Editor_Page_0.java:40) ~[?:?]
at fancyui.craterhater.uibuilder.Editor$1.call(Editor.java:36) ~[?:?]
at fancyui.craterhater.masterfancyui.FancyUI.openInventory(FancyUI.java:195) ~[?:?]
at fancyui.craterhater.uibuilder.Editor.createEditorGUI(Editor.java:236) ~[?:?]
at fancyui.craterhater.commandhandler.MasterCommand$2.call(MasterCommand.java:69) ~[?:?]
at fancyui.craterhater.commandhandler.CECommand.playerExecutes(CECommand.java:105) ~[?:?]
at fancyui.craterhater.commandhandler.CECommand.checkPlayerExecutes(CECommand.java:98) ~[?:?]
at fancyui.craterhater.commandhandler.MasterCommand.onCommand(MasterCommand.java:153) ~[?:?]
at org.bukkit.command.PluginCommand.execute(PluginCommand.java:44) ~[1.8.jar:git-Spigot-870264a-0a645a2]
... 15 more
Line 13 of the ComponentMaker class simply says;
FancyComponent fancyComponent = new FancyComponent();
I did some debugging and decided to print the following;
System.out.println(FancyComponent.class.toGenericString());
This printed;
public abstract class fancyui.craterhater.components.FancyComponent
While the FancyComponent class is not abstract. Here is the FancyComponent class:
public class FancyComponent {
//FancyComponent class represents the individual items placed in a FancyUI.
//It handles things such as animation, itemstack building and event handling whilst
//allowing the FancyUI to handle loading and deleting of these components.
//FancyComponents are added to the FancyUI by the user of the API using LoadFunctions.
//They can also be generated from a FancyUI file.
private FancyLocation fancyLocation;
private FancyItemStack fancyItemStack;
private FancyDisplayType fancyDisplayType;
private String identifier = null;
private int ID = 0;
private int basicSlot = 0;
private boolean copied;
public FancyComponent() {
fancyDisplayType = FancyDisplayType.DYNAMIC;
}
public FancyComponent copy(FancyLocation newLocation) {
FancyComponent newComponent = new FancyComponent();
newComponent.setDisplayType(fancyDisplayType);
newComponent.setID(ID);
newComponent.setFancyItemStack(fancyItemStack);
newComponent.setFancyLocation(newLocation);
newComponent.setAttributes(attributes);
newComponent.setIdentifier(identifier);
return newComponent;
}
//Method gets called when FancyUI decides to load in new FancyComponents.
//This happens when openInventory gets called and new components are generated.
//This method should start future animations. It should also handle ItemStack placement.
//The boolean here represents whether or not it has succesfully been added. This is used in the
//FancyUI in order to keep track of all active components and not the once off-screen.
public boolean addToUI(Inventory inventory, Player p, FileConfiguration fc, FileConfiguration fc2) {
if(fancyLocation == null) {return false;}
return addToUI(inventory, p, fancyLocation, fc, fc2);
}
public boolean addToUI(Inventory inventory, Player p, FancyLocation fancyLocation, FileConfiguration fc, FileConfiguration fc2) {
if(fancyItemStack == null) {return false;}
FancyUI fancyUI = fancyLocation.getFancyUI();
FancyLocation masterFancyLocation = fancyUI.getFancyData().getCurrentFancyLocation();
if(!fancyLocation.isInView(masterFancyLocation, fancyDisplayType)) {return false;}
int basicSlot = fancyLocation.getBasicLocation(masterFancyLocation.getX(), masterFancyLocation.getY(), fancyDisplayType);
if(basicSlot < 0 || basicSlot >= inventory.getSize()) {return false;}
this.basicSlot = basicSlot;
ItemStack itemStack = fancyItemStack.buildItemStack(p,fc2);
if(fc != null) {
if(fc.contains("Components."+identifier+".Animations")) {
for(String animation : fc.getConfigurationSection("Components."+identifier+".Animations").getKeys(false)) {
if(fc.getBoolean(("Components."+identifier+".Animations."+animation+".continious"))){
List<String> frames = new ArrayList<>();
if(fc.contains("Components."+identifier+".Animations."+animation+".animation")) {
frames = fc.getStringList("Components."+identifier+".Animations."+animation+".animation");
}
int interval = fc.getInt("Components."+identifier+".Animations."+animation+".interval");
if(interval < 1) {
interval = 2;
}
int position = fc.getInt("Components."+identifier+".Animations."+animation+".position");
boolean continious = fc.getBoolean("Components."+identifier+".Animations."+animation+".continious");
boolean reverse = fc.getBoolean("Components."+identifier+".Animations."+animation+".reverse");
fancyItemStack.startAnimation(frames, p, interval, position, this, continious, reverse);
break;
}
}
}
}
if(fancyLocation.getFancyUI().getFancyData().isPlayerInventory()) {
if(fancyItemStack.getCustomName().getFancyStringAnimations().length > 0) {
fancyItemStack.startAnimation(fancyItemStack.getCustomName().getFancyStringAnimations()[0].getFrames(), p, 4, 0, this, true, false);
}
}
inventory.setItem(basicSlot, itemStack);
return true;
}
//Method to reload just this component. It is used when cycling through fancyLore. If a function makes changes
//to other components the reload method in the FancyUI has to be called in order to fully reload a page.
public void reload(Player p) {
FileConfiguration fc = DataHandler.getFile(96,p.getUniqueId().toString(), false, "Data", "Users");
ItemStack itemStack = fancyItemStack.buildItemStack(p,fc);
if(fancyLocation == null) {
return;
}
if(fancyLocation.getFancyUI().getFancyData().isPlayerInventory()) {
p.getInventory().setItem(basicSlot, itemStack);
return;
}
fancyLocation.getFancyUI().getFancyData().getInventory().setItem(basicSlot, itemStack);
}
//Method gets called when FancyUI deletes all information on other FancyComponents.
//This happens when openInventory gets called and new components are generated.
//Make sure that this method cleans up future animations so they don't go around throwing nullpointers.
//The ItemStack itself is removed automatically by the FancyUI.
public void notifyOfDeletion() {
fancyItemStack.stopAnimating();
}
//Attributes are pieces of code written by the user of the API in order to have certain things
//happen at certain events such as ON_CLICK or ON_DROP etc.
private List<Attribute> attributes = new ArrayList<>();
public void addAttribute(Attribute attribute) {
attributes.add(attribute);
}
public List<Attribute> getAttributes(){
return attributes;
}
public void setAttributes(List<Attribute> attributes) {
this.attributes = attributes;
}
//Method to identify which Attributes need to run at these specific occasions. These occasions
//may include things suchs as ON_CLICK or ON_1 just to name a few. This method consists of two
//distinct parts. The first part handles all generated components which have scripts assigned to them
//and the second part handles all components that have been generated by the plugin itself. And thus
//do not have scripts assigned to them.
public void handleEvent(FileConfiguration fc, FileConfiguration fc2, FunctionParams params, AttributeOccasion... attributeOccasions) {
//The 'identifier' is a tag all generated components get. It is unique to all components.
//Non-generated components do not have this tag and thus we can check for null to see whether or not
//it is generated.
if(identifier != null) {
if(fc.contains("Components."+identifier+".Scripts")) {
for(String script : fc.getConfigurationSection("Components."+identifier+".Scripts").getKeys(false)) {
if(fc.contains("Components."+identifier+".Scripts."+script+".events")) {
List<String> scriptLines = fc.getStringList("Components."+identifier+".Scripts."+script+".script");
for(String event : fc.getStringList("Components."+identifier+".Scripts."+script+".events")) {
A: for(AttributeOccasion attributeOccasion : attributeOccasions) {
if(attributeOccasion.name().equalsIgnoreCase(event)) {
fancyLocation.getFancyUI().getScriptParser().executeScript(params.getPlayer(), scriptLines, FancyUI.fancyUI.get(params.getPlayer().getUniqueId()), params.getFancyComponent(), fc2, false);
break A;
}
}
}
}
}
}
if(fc.contains("Components."+identifier+".Animations")) {
for(String script : fc.getConfigurationSection("Components."+identifier+".Animations").getKeys(false)) {
if(fc.contains("Components."+identifier+".Animations."+script+".events")) {
for(String event : fc.getStringList("Components."+identifier+".Animations."+script+".events")) {
A: for(AttributeOccasion attributeOccasion : attributeOccasions) {
if(attributeOccasion.name().equalsIgnoreCase(event)) {
List<String> frames = new ArrayList<>();
if(fc.contains("Components."+identifier+".Animations."+script+".animation")) {
frames = fc.getStringList("Components."+identifier+".Animations."+script+".animation");
}
int interval = fc.getInt("Components."+identifier+".Animations."+script+".interval");
if(interval < 1) {
interval = 2;
}
int position = fc.getInt("Components."+identifier+".Animations."+script+".position");
boolean continious = fc.getBoolean("Components."+identifier+".Animations."+script+".continious");
boolean reverse = fc.getBoolean("Components."+identifier+".Animations."+script+".reverse");
fancyItemStack.startAnimation(frames, params.getPlayer(), interval, position, this, continious, reverse);
break A;
}
}
}
}
}
}
}
//Handles all components generated by the plugin as well as generated components from file if this component is inside of an editor.
//If inside an editor generated components will have some attributes linked to them by the plugin. If not inside an editor, generated components
//will not have any attributes linked to them.
A:for(Attribute attribute : attributes) {
for(AttributeOccasion attributeOccasion : attribute.getAttributeOccasions()){
for(AttributeOccasion attributeOccasion2 : attributeOccasions) {
if(attributeOccasion.equals(attributeOccasion2)) {
attribute.getAttributeFunction().call(params);
continue A;
}
}
}
}
}
//This method is used to move the cursor in list cycleables.
public void performCursorMove(int delta, Player p) {
if(fancyItemStack == null) {return;}
FancyLore fancyLore = fancyItemStack.getFancyLore();
if(fancyLore == null) {return;}
fancyLore.moveCursor(delta, p);
}
public void setCursorPosition(int n, Player p) {
if(fancyItemStack == null) {return;}
FancyLore fancyLore = fancyItemStack.getFancyLore();
if(fancyLore == null) {return;}
fancyLore.setCursorPosition(n, p);
}
public int getSlot() {
return basicSlot;
}
public FancyLocation getFancyLocation() {
return fancyLocation;
}
public FancyItemStack getFancyItemStack() {
return fancyItemStack;
}
public void setFancyItemStack(FancyItemStack fancyItemStack) {
this.fancyItemStack = fancyItemStack;
}
public void setFancyLocation(FancyLocation fancyLocation) {
this.fancyLocation = fancyLocation;
}
public void setDisplayType(FancyDisplayType fancyDisplayType) {
this.fancyDisplayType = fancyDisplayType;
}
public void setIdentifier(String identifier) {
this.identifier = identifier;
}
public String getIdentifier() {
return identifier;
}
public void setIsCopied(boolean copied) {
this.copied = copied;
}
public boolean isCopied() {
return copied;
}
public void setID(int ID) {
this.ID = ID;
}
public int getID() {
return ID;
}
public FancyDisplayType getFancyDisplayType() {
return fancyDisplayType;
}
}
The weird thing is that this does not occur on later versions of Spigot. I have tested this on 1.8, 1.9, and 1.15 and the error occurs on 1.8 and 1.9 but not on 1.15. Any help would be appreciated.
It appears to only not throw the error on the latest version (1.15). All other versions do throw the error.
After a few hours of testing I decided to copy paste the code of the problematic class into a new class. I renamed all references to this class. This fixed my issue but I am not particularly happy with it especially because the original name is still causing the error. So if I rename the new class to the old name the error reappears.
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.
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.
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);
});
}