My code is about the game "Minecraft". I want a Array item list to drop random items, what works fine.
I am trying to set up a kind of scheduler for an EventHandler.
I want the EventHandler to be executed only 5 times a minute, or every 12 seconds or something.
If I work with a "Bukkit" "runTaskLater" function, the Code is executed with a delay, but after the delay it's running permanent.
Here you have my raw code without any Scheduler.
#EventHandler
public void on(PlayerMoveEvent e) {
Player p = e.getPlayer();
if(p.getLocation().getBlock().getType() == Material.STONE_PLATE) {
if(p.getLocation().subtract(0D, 1D, 0D).getBlock().getType() == Material.STAINED_CLAY) {
Block block = p.getLocation().getBlock();
Random ran = new Random();
int auswahl = ran.nextInt(2);
int zahl = ran.nextInt(main.Drops.size());
ItemStack itemstack = main.Drops.get(zahl);
block.getWorld().dropItemNaturally(p.getLocation(), itemstack);
}
}
}
and now this handler should be performed only every 12 seconds.
Do anyone have a solution for me?
Thanks a lot!
As i understand it, you want to have a cooldown. Just store the time of the last event in a variable and check if the current time is 12 seconds higher:
private long lastTime = System.currentTimeMillis();
#EventHandler
public void on(PlayerMoveEvent e) {
if (lastTime < System.currentTimeMillis() - 12000) {
Player p = e.getPlayer();
if(p.getLocation().getBlock().getType() == Material.STONE_PLATE) {
if(p.getLocation().subtract(0D, 1D, 0D).getBlock().getType() == Material.STAINED_CLAY) {
Block block = p.getLocation().getBlock();
Random ran = new Random();
int auswahl = ran.nextInt(2);
int zahl = ran.nextInt(main.Drops.size());
ItemStack itemstack = main.Drops.get(zahl);
block.getWorld().dropItemNaturally(p.getLocation(), itemstack);
}
}
lastTime = System.currentTimeMillis();
}
}
If it does not work, please comment :)
Related
I have the following code.
I am trying to:
Assign a bukkit runnable task to a given ID
Assign a player a given ID
Place these two IDs into a hashmap, where each participant is matched to their respective bukkit runnable task
The repeating task should assign a maximum of 4 objects to a given player's inventory, assigning one object every minute.
This means that for each player, the repeating task should last a maximum of 4 minutes and should be cancelled when the counter exceeds the length of the hashmap.
However, I get the issue 'the local variable task may not have been initialised'.
I know that this means that I should initialise the variable 'task', but I am not sure how to do so, given that the variable task corresponds to the bukkit runnable task?
I would be so grateful for a helping hand!
Map<UUID, Integer> map = new HashMap<UUID, Integer>();
List<ItemStack> items = java.util.Arrays.asList(
new ItemStack(Material.WATER),
new ItemStack(Material.COBWEB),
new ItemStack(Material.CAKE),
new ItemStack(Material.RED_WOOL)
);
#EventHandler
public void on(PlayerQuitEvent event) {
map.remove(event.getPlayer());
}
#EventHandler
public void on(PlayerInteractEvent event) {
final ItemStack item = event.getItem();
if (item.getType() == Material.WHITE_WOOL) {
BukkitTask task = getServer().getScheduler().runTaskTimer(this, () -> {
if(this.stopRepeater) {
int counter = 0;
while (counter <= 4){
Material[] listofitems = {Material.WATER, Material.COBWEB, Material.CAKE, Material.SNOW};
int idx = counter;
Material randomItem = listofitems[idx];
ItemStack items = new ItemStack(randomItem);
Player thePlayer = event.getPlayer();
thePlayer.getInventory().addItem(items);
map.put(event.getPlayer().getUniqueId(),task.getTaskId());
counter ++;
if (counter >= map.size()) {
Bukkit.getServer().getScheduler().cancelTask(task.getTaskId());
}
}
}
}, 20 * 60, 20 * 60);
}
}
You might be able to get around this by splitting the BukkitTask task = ... line into BukkitTask task; and task = ..., though I've not tested it.
Thank you for your help!
I have come up with the following solution:
#EventHandler
public void onPlayerInteract(PlayerInteractEvent event) {
new BukkitRunnable() {
int count = 0;
Material[] listofitems = {Material.WATER_BUCKET, Material.CACTUS, Material.CAKE, Material.SNOW};
public void run() {
if (count == listofitems.length-1) cancel();
Material nextItem = listofitems[count];
ItemStack item = new ItemStack(nextItem);
event.getPlayer().getInventory().addItem(item);
count++;
}
}.runTaskTimer(this, 1200L, 1200L);
}
}
v
The lib I'm using is processing library. I'm currently making a game and I need to remove items from list every 2 seconds after adding it (item will be add by Keypressed SPACE). I've tried this:
List<String> items = new ArrayList<>();
boolean timeStarted;
int timeDuration = 2000;
int startTime = 0;
if (timeStarted) {
int currentDuration = millis() - startTime;
if (currentDuration >= timeDuration) {
items.remove(0);
startTime = millis();
timeStarted = false;
}
}
public void keyPressed(KeyEvent e) {
int keycode = e.getKeyCode();
if (keycode == 32) { // SPACE
startTime = millis();
timeStarted = true;
items.add("new item");
}
But this only remove the first item in the list, and it will stuck if I press the SPACE too fast (add too many items at the same time).
Could anyone help me or give me some idea how to deal with this? Thanks in advance!
I personally wouldn't mix and match threading and Processing together.
If you're using Processing, and are doing this for a game (thus probably dont require milisecond precision), why not just use the framerate as a time measurement? Your Processing programs are always supposed to run at a set frameRate (unless you have some performance issues). You also won't see anything happen untill the frame where the action happens has been drawn anyway, so it doesn't really matter if your action completed halfway between a frame, your end user won't see the difference.
The default frameRate in Processing is 30fps. You can set a custom rate using the frameRate() function (docs). frameCount returns the number of frames drawn (0 on the first frame).
Thus, you can do something like the following pseudocode
int removeItemCheckpoint;
int removeItemDuration;
void setup()
{
// 30 frames per second
frameRate(30);
// window is two seconds, so this duration will last two times the frameRate
removeItemDuration = frameRate * 2;
}
void draw()
{
if (some input)
{
// set checkpoint to current frame count + the duration
removeItemCheckpoint = frameCount + removeItemDuration;
}
// if current frame count is larger than the checkpoint, perform action
if (frameCount > removeItemCheckpoint)
{
// set checkpoint to pseudo infinity
removeItemCheckpoint = Integer.MAX_VALUE;
// remove item here
}
}
If you want to handle this for multiple objects at a time, you could create something like a list of created list index/checkpoint pairs and loop over those instead.
List<Pair<int, int>> toBeRemoved = new List<Pair<int, int>>();
// add a new pair
toBeRemoved.add(new Pair(itemIndex, frameCount + removeItemDuration));
// loop over the list
foreach (Pair<int, int> item in toBeRemoved)
{
if (frameCount > item.getKey())
{
itemList.remove(item.getValue());
}
}
Use while for checking element from list 2 seconds after adding.
// remove element from list 2 seconds after adding it
while (startedTime+timeDuration >= System.currentTimeMillis()) {
// If delete exit while
items.remove(0);
break;
}
and Use thread for removing your item when you can keypress space.
if (keycode == 32) { // SPACE
long startTime = System.currentTimeMillis();
items.add("new item");
// Thread execute
Thread thread = new Thread(new Remover(startTime, items));
thread.start();
}
#Override
public void run() {
// remove element from list 2 seconds after adding it
while (startedTime+timeDuration >= System.currentTimeMillis()) {
// If delete exit while
items.remove(0);
break;
}
// whether item is deleted
System.out.println(items);
}
Full code
public class Main {
public static void main(String[] args) {
Main main = new Main();
main.keyPressed(32);
}
// Integer instead of Keycode
public void keyPressed(Integer keycode) {
List<String> items = new ArrayList<String>();
if (keycode == 32) { // SPACE
long startTime = System.currentTimeMillis();
items.add("new item");
// Check items
System.out.println(items);
// Thread execute
Thread thread = new Thread(new Remover(startTime, items));
thread.start();
}
}
}
class Remover implements Runnable{
List<String> items;
int timeDuration = 2000;
long startedTime;
public Remover(long startedTime,List<String> items) {
this.startedTime = startedTime;
this.items = items;
}
#Override
public void run() {
// remove element from list 2 seconds after adding it
while (startedTime+timeDuration >= System.currentTimeMillis()) {
// If delete items,Exit while
items.remove(0);
break;
}
// whether item is deleted
System.out.println(items);
}
}
This TestCode is supposed to create an stream of numbers in seconds.
Collect 10 samples, and average the time which each samples comes out.
I did try to use if-else, but the variable from if doesn't share with else.
Please correct me if I'm wrong.
I don't understand lambda just yet.
public class TestCode {
private int eachTwoSec;
// supposed to aList.add 10 items
// average the time needed in between each aList.add (2 obviously)
public void avgTimeTaken() {
ArrayList aList = new ArrayList();
for (int i = 0; i < 10; i++) {
aList.add(eachTwoSec);
}
}
// return a number every two seconds (endless stream of samples)
// samples 50,52,54,56,58,60,2,4,6,8,10
public void twoSecTime() {
try {
Thread.sleep(2000);
} catch (InterruptedException ex) {
Logger.getLogger(Dummies.class.getName()).log(Level.SEVERE, null, ex);
}
LocalDateTime ldt = LocalDateTime.now();
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("ss");
eachTwoSec = Integer.parseInt(ldt.format(dtf));
System.out.println(eachTwoSec);
twoSecTime();
}
public TestCode() {
// construct
avgTimeTaken();
new Thread(this::twoSecTime).start();
}
public static void main(String[] args) {
// just a start point
new TestCode();
}
}
The literal answer to the question "How do I average the contents in ArrayList?" for a List<Integer> is:
list.stream().mapToInt(Integer::intValue).average();
Though I suspect that's not really what you need to know given the concurrency issues in your code.
This may help to do what you want (or give you a place from which to proceed).
I use a timer to take action every 2000 ms. I prefer using the Swing timer and not messing around with TimerTasks.
I don't just add 2 sec but grab the current nanoSecond time
This introduces latency errors introduced by various parts of the code and
of synchronicities.
I add the microseconds to the ArrayList. These are in the form of delta from the most recent to the previously recorded value.
and when count == 10 I stop the timer and invoke the averaging method.
Most of the work is done on the EDT (normally a bad thing but okay for this exercise). If that were a problem, another thread could be started to handle the load.
I then use the original main thread to signal wait to leave the JVM. Imo, preferred over System.exit(0);
The gathered data and final average are all in microseconds.
import java.util.ArrayList;
import javax.swing.Timer;
public class TestCode {
Timer timer;
int delay = 2000; // milliseconds
int count = 0;
long last;
ArrayList<Integer> aList = new ArrayList<>();
Object mainThread;
public void avgTimeTaken() {
double sum = 0;
for (Integer secs : aList) {
sum += secs;
}
System.out.println("Avg = " + sum / aList.size());
}
public void twoSecTime() {
long now = System.nanoTime();
int delta = (int) (now / 1000 - last / 1000); // microseconds
last = now;
aList.add(delta);
System.out.println(delta);
count++;
if (count == 10) {
// stop the time
timer.stop();
// get the averages
avgTimeTaken();
// wake up the wait to exit the JVM
// twoSecTime is run on the EDT via timer
// so need to use mainThread
synchronized (mainThread) {
mainThread.notifyAll();
}
}
}
public static void main(String[] args) {
new TestCode().start();
}
public void start() {
mainThread = this;
timer = new Timer(2000, (ae) -> twoSecTime());
last = System.nanoTime(); // initialize last
timer.start();
synchronized (this) {
try {
wait(); // main thread waiting until all is done
} catch (InterruptedException ie) {
ie.printStackTrace();
}
}
}
}
I'm working on a sudoku solver in javafx and I need to show the algorithm solving. My board is a bunch of buttons laid out in a 9x9 grid and the user enters the puzzle and I solve it.
The solving works fine but I cannot get the algorithm to run. This is what I have but whenever I run the program the window stops responding and I have to terminate the window and get an interrupted by signal 2: SIGINT.
private void loop(Sequence s) {
long currentTime = System.currentTimeMillis();
long lastTime = System.currentTimeMillis();
int x = s.getX();
int y = s.getY();
String n = Integer.toString(s.getNum());
Button button = buttons.get(x).get(y);
boolean isFinished = true;
while(isFinished) {
if(currentTime > lastTime + 400){
button.setText(n);
isFinished = false;
} else {
currentTime = System.currentTimeMillis();
}
}
public void show(){
for (Sequence s : sequences){
loop(s);
}
}
Sorry if this is a very closed question that won't be useful to others, but I'm just stumped by this bug and I haven't been able to solve it for weeks!
I am working on a wave based survival game and am currently working on a spawning mechanism.
The code I wrote works perfectly for one wave, but somehow doesn't restart for further waves.
I have written the code below:
public void run() {
while (ingame) {
if (enemyList.isEmpty()) {
stopSpawn = false;
try {
Thread.sleep(500);
spawnNewEnemy();
} catch (InterruptedException ex) {
System.out.println("Interrupted");
}
} else {
if (!enemyList.isEmpty() && !stopSpawn) {
// getEnemyAmount returns the amount of enemies that should be spawned this wave
for (int enemiesSpawned = 0; enemiesSpawned < getEnemyAmount(); enemiesSpawned++) {
try {
Thread.sleep(500);
} catch (InterruptedException ex) {
}
System.out.println(currentWave);
spawnNewEnemy();
}
stopSpawn = true;
}
}
}
}
Here is the spawnNewEnemy method
public void spawnNewEnemy() {
Random spawn = new Random();
int spawnX = spawn.nextInt(500);
int spawnXTest = b.bX - spawnX;
if (spawnXTest < 20) {
spawnX = spawnX + 20;
} else {
}
int spawnY = spawn.nextInt(500);
int spawnYTest = b.bX - spawnY;
if (spawnYTest < 20) {
spawnY = spawnY + 20;
} else {
}
spawnY = spawnY + 20;
spawnX = spawnX + 20;
enemyList.add(new Enemy(spawnX, spawnY));
}
I can read the following in your code:
If the list of enemies is empty, you set stopSpawn to false and spawn an enemy.
That triggers your else-statement.
There, you spawn enemies based on the enemy count.
stopSpawn is set to true, thus your else-statement doesn't get triggered anymore.
Nothing happens anymore untill your enemylist is empty.
If your enemylist is empty again, you start over.
The logics seems ok, so I'm thinking either the way you spawn enemies through spawnNewEnemy() is faulty, or the way you remove enemies from the enemyList is faulty. I see neither of that code so that is as far as I can go in this answer.
I guess your problem with a loop is in stopSpawn value.
You set it to true after the first wave and likely not setting to `false' before starting the next wave.