/*
* This file is part of NucleusFramework for Bukkit, licensed under the MIT License (MIT).
*
* Copyright (c) JCThePants (www.jcwhatever.com)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.jcwhatever.nucleus.utils.performance.queued;
import com.jcwhatever.nucleus.managed.scheduler.Scheduler;
import com.jcwhatever.nucleus.managed.scheduler.IScheduledTask;
import com.jcwhatever.nucleus.managed.scheduler.TaskHandler;
import org.bukkit.plugin.Plugin;
/**
* Abstract implementation of a {@link QueueTask} designed to make it easier to
* perform iterative tasks over 3D arrays/coordinates.
*
* <p>Iterative tasks can be broken up into segments which are performed like individual
* tasks; run in consecutive order with a delay between each segment run.</p>
*
* <p>Note: The 3D data is iterated from smallest value to largest value.</p>
*/
public abstract class Iteration3DTask extends QueueTask {
private long _segmentSize;
private long _segmentsCompleted;
private long _iterations;
private int _xStart, _yStart, _zStart, _xEnd, _yEnd, _zEnd;
private int _xCurrent, _yCurrent, _zCurrent;
private long _volume;
private final Object _sync = new Object();
private IScheduledTask _task;
/**
* Constructor. Initializes the 3D task parameters.
*
* @param plugin The owning plugin
* @param concurrency The task concurrency. (Main thread or Async)
* @param segmentSize The number of iterations to perform before pausing
* @param xStart The x value to start from
* @param yStart The y value to start from
* @param zStart The z value to start from
* @param xEnd The x value to end at
* @param yEnd The y value to end at
* @param zEnd The z value to end at
*/
public Iteration3DTask(Plugin plugin, TaskConcurrency concurrency,
long segmentSize, int xStart, int yStart, int zStart,
int xEnd, int yEnd, int zEnd) {
super(plugin, concurrency);
_xCurrent = _xStart = Math.min(xStart, xEnd);
_xEnd = Math.max(xStart, xEnd);
_yCurrent = _yStart = Math.min(yStart, yEnd);
_yEnd = Math.max(yStart, yEnd);
_zCurrent =_zStart = Math.min(zStart, zEnd);
_zEnd = Math.max(zStart, zEnd);
_volume = (_xEnd - _xStart) * (_yEnd - _yStart) * (_zEnd - _zStart);
_segmentSize = segmentSize;
}
/**
* Get the starting coordinates X value.
*/
public final int getXStart() {
return _xStart;
}
/**
* Get the starting coordinates Y value.
*/
public final int getYStart() {
return _yStart;
}
/**
* Get the starting coordinates Z value.
*/
public final int getZStart() {
return _zStart;
}
/**
* Get the end coordinates X value.
*/
public final int getXEnd() {
return _xEnd;
}
/**
* Get the end coordinates Y value.
*/
public final int getYEnd() {
return _yEnd;
}
/**
* Get the end coordinates Z value.
*/
public final int getZEnd() {
return _zEnd;
}
/**
* Get the current X index.
*/
public final int getXCurrent() {
return _xCurrent;
}
/**
* Get the current Y index.
*/
public final int getYCurrent() {
return _yCurrent;
}
/**
* Get the current Z index.
*/
public final int getZCurrent() {
return _zCurrent;
}
/**
* Get the total number of segments completed.
*/
public final long getSegmentsCompleted() {
return _segmentsCompleted;
}
/**
* Get the size of an iterated segment
*/
public final long getSegmentSize() {
return _segmentSize;
}
/**
* Get the total number of iterations completed.
*/
public final long getIterations() {
return _iterations;
}
/**
* Get the total volume of the 3D cuboid.
*/
public final long getVolume() {
return _volume;
}
@Override
protected final void onRun() {
onIterateBegin();
if (_task != null)
_task.cancel();
_task = Scheduler.runTaskRepeat(getPlugin(), 1, 1, new Iterator3D());
}
/**
* Invoked for each 3D value set that is iterated over.
*
* @param x The current x value.
* @param y The current y value.
* @param z The current z value.
*/
protected abstract void onIterateItem(final int x, final int y, final int z);
/**
* Invoked before the task is first run.
*
* <p>Intended for optional override.</p>
*/
protected void onIterateBegin() {}
/**
* Invoked before iteration begins on a new segment.
*
* <p>Intended for optional override.</p>
*
* @param x The current x value.
* @param y The current y value.
* @param z The current z value.
*/
protected void onSegmentStart(int x, int y, int z) {}
/**
* Invoked after iteration over a segment ends.
*
* <p>Intended for optional override.</p>
*
* @param x The ending x value.
* @param y The ending y value.
* @param z The ending z value.
*/
protected void onSegmentEnd(int x, int y, int z) {}
/**
* Invoked just before the task finishes.
*
* <p>Intended for optional override.</p>
*/
protected void onPreComplete() {}
// The worker that performs the iterations
private class Iterator3D extends TaskHandler {
@Override
public void run() {
if (isEnded()) {
cancelTask();
return;
}
synchronized (_sync) {
boolean isStart = true;
int completed = 0;
for (int y = isStart ? _yCurrent : _yStart; y <= _yEnd; y++) {
for (int x = isStart ? _xCurrent : _xStart; x <= _xEnd; x++) {
for (int z = isStart ? _zCurrent : _zStart; z <= _zEnd; z++) {
// check for end of segment
if (_segmentSize > 0 && completed >= _segmentSize) {
_segmentsCompleted ++;
_xCurrent = x;
_yCurrent = y;
_zCurrent = z;
onSegmentEnd(x, y, z);
// end segment
return;
}
// check if this is the start of the segment
if (isStart) {
onSegmentStart(x, y, z);
}
isStart = false;
onIterateItem(x, y, z);
if (!isRunning()) {
cancelTask();
return;
}
_iterations++;
completed++;
}
}
}
}
onPreComplete();
cancelTask();
}
@Override
protected void onCancel() {
complete();
}
}
}