001/*
002 * PlotSquared, a land and world management plugin for Minecraft.
003 * Copyright (C) IntellectualSites <https://intellectualsites.com>
004 * Copyright (C) IntellectualSites team and contributors
005 *
006 * This program is free software: you can redistribute it and/or modify
007 * it under the terms of the GNU General Public License as published by
008 * the Free Software Foundation, either version 3 of the License, or
009 * (at your option) any later version.
010 *
011 * This program is distributed in the hope that it will be useful,
012 * but WITHOUT ANY WARRANTY; without even the implied warranty of
013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
014 * GNU General Public License for more details.
015 *
016 * You should have received a copy of the GNU General Public License
017 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
018 */
019package com.plotsquared.core.util.task;
020
021import org.checkerframework.checker.nullness.qual.NonNull;
022import org.checkerframework.checker.nullness.qual.Nullable;
023
024import java.util.Collection;
025import java.util.HashMap;
026import java.util.HashSet;
027import java.util.Iterator;
028import java.util.Map;
029import java.util.Set;
030import java.util.concurrent.Callable;
031import java.util.concurrent.CompletableFuture;
032import java.util.concurrent.Future;
033import java.util.concurrent.atomic.AtomicInteger;
034
035/**
036 * Task manager that handles scheduling of tasks.
037 * Synchronous methods make no guarantee of being scheduled on the
038 * server thread, instead they guarantee that no two synchronous
039 * operations happen at the same time. Implementations of
040 * the task manager might make other guarantees. All asynchronous
041 * operations will happen on another thread, no matter where
042 * they're scheduled from.
043 */
044public abstract class TaskManager {
045
046    private static final Set<String> teleportQueue = new HashSet<>();
047    private static final Map<Integer, PlotSquaredTask> tasks = new HashMap<>();
048    public static AtomicInteger index = new AtomicInteger(0);
049
050    private static TaskManager platformImplementation;
051
052    /**
053     * Add a string to the teleport queue
054     *
055     * @param string String to add
056     */
057    public static void addToTeleportQueue(final @NonNull String string) {
058        teleportQueue.add(string);
059    }
060
061    /**
062     * Remove a string from the teleport queue
063     *
064     * @param string String to remove
065     *               return {@code true} if the value was stored in the map, or {@code false}
066     * @return if string was actually removed
067     */
068    public static boolean removeFromTeleportQueue(final @NonNull String string) {
069        return teleportQueue.remove(string);
070    }
071
072    /**
073     * Add a task to the task map
074     *
075     * @param task Task
076     * @param id   Task ID
077     */
078    public static void addTask(final @NonNull PlotSquaredTask task, final int id) {
079        tasks.put(id, task);
080    }
081
082    /**
083     * Remove a task from the task map and return the stored value
084     *
085     * @param id Task ID
086     * @return Task if stored, or {@code null}
087     */
088    public static @Nullable PlotSquaredTask removeTask(final int id) {
089        return tasks.remove(id);
090    }
091
092    /**
093     * Run a repeating synchronous task. If using a platform scheduler,
094     * this is guaranteed to run on the server thread
095     *
096     * @param runnable Task to run
097     * @param taskTime Task interval
098     * @return Created task object, can be used to cancel the task
099     */
100    public static @NonNull PlotSquaredTask runTaskRepeat(
101            final @Nullable Runnable runnable,
102            final @NonNull TaskTime taskTime
103    ) {
104        if (runnable != null) {
105            if (getPlatformImplementation() == null) {
106                throw new IllegalArgumentException("disabled");
107            }
108            return getPlatformImplementation().taskRepeat(runnable, taskTime);
109        }
110        return PlotSquaredTask.nullTask();
111    }
112
113    /**
114     * Run an asynchronous task. This will never run on the server thread
115     *
116     * @param runnable Task to run
117     */
118    public static void runTaskAsync(final @Nullable Runnable runnable) {
119        if (runnable != null) {
120            if (getPlatformImplementation() == null) {
121                runnable.run();
122                return;
123            }
124            getPlatformImplementation().taskAsync(runnable);
125        }
126    }
127
128    /**
129     * Run a synchronous task. If using a platform scheduler, this is guaranteed
130     * to run on the server thread
131     *
132     * @param runnable Task to run
133     */
134    public static void runTask(final @Nullable Runnable runnable) {
135        if (runnable != null) {
136            if (getPlatformImplementation() == null) {
137                runnable.run();
138                return;
139            }
140            getPlatformImplementation().task(runnable);
141        }
142    }
143
144    /**
145     * Run a synchronous task after a given delay.
146     * If using a platform scheduler, this is guaranteed to run on the server thread
147     *
148     * @param runnable Task to run
149     * @param taskTime Task delay
150     */
151    public static void runTaskLater(
152            final @Nullable Runnable runnable,
153            final @NonNull TaskTime taskTime
154    ) {
155        if (runnable != null) {
156            if (getPlatformImplementation() == null) {
157                runnable.run();
158                return;
159            }
160            getPlatformImplementation().taskLater(runnable, taskTime);
161        }
162    }
163
164    /**
165     * Run an asynchronous task after a given delay. This will never
166     * run on the server thread
167     *
168     * @param runnable Task to run
169     * @param taskTime Task delay
170     */
171    public static void runTaskLaterAsync(
172            final @Nullable Runnable runnable,
173            final @NonNull TaskTime taskTime
174    ) {
175        if (runnable != null) {
176            if (getPlatformImplementation() == null) {
177                runnable.run();
178                return;
179            }
180            getPlatformImplementation().taskLaterAsync(runnable, taskTime);
181        }
182    }
183
184    public static @Nullable TaskManager getPlatformImplementation() {
185        return platformImplementation;
186    }
187
188    public static void setPlatformImplementation(final @NonNull TaskManager implementation) {
189        platformImplementation = implementation;
190    }
191
192    /**
193     * Break up a series of tasks so that they can run without lagging the server
194     *
195     * @param objects Objects to perform the task on
196     * @param task    Task to perform
197     * @param <T>     Object type
198     * @return Future that completes when the tasks are done
199     */
200    public <T> CompletableFuture<Void> objectTask(
201            final @NonNull Collection<T> objects,
202            final @NonNull RunnableVal<T> task
203    ) {
204        final Iterator<T> iterator = objects.iterator();
205        final ObjectTaskRunnable<T> taskRunnable = new ObjectTaskRunnable<>(iterator, task);
206        TaskManager.runTask(taskRunnable);
207        return taskRunnable.getCompletionFuture();
208    }
209
210    /**
211     * Make a synchronous method call and return the result
212     *
213     * @param function Method to call
214     * @param <T>      Return type
215     * @return Method result
216     * @throws Exception If the call fails
217     */
218    public <T> T sync(final @NonNull Callable<T> function) throws Exception {
219        return sync(function, Integer.MAX_VALUE);
220    }
221
222    /**
223     * Make a synchronous method call and return the result
224     *
225     * @param function Method to call
226     * @param timeout  Timeout (ms)
227     * @param <T>      Return type
228     * @return Method result
229     * @throws Exception If the call fails
230     */
231    public abstract <T> T sync(final @NonNull Callable<T> function, final int timeout)
232            throws Exception;
233
234    /**
235     * Call a method synchronously and return a future with
236     * the result of the result
237     *
238     * @param method Method to be ran synchronously
239     * @param <T>    Return type
240     * @return Future completing with the result
241     */
242    public abstract <T> Future<T> callMethodSync(final @NonNull Callable<T> method);
243
244    /**
245     * Run a repeating synchronous task. If using a platform scheduler,
246     * this is guaranteed to run on the server thread
247     *
248     * @param runnable Task to run
249     * @param taskTime Task interval
250     * @return Created task object, can be used to cancel the task
251     */
252    public abstract PlotSquaredTask taskRepeat(
253            @NonNull Runnable runnable,
254            @NonNull TaskTime taskTime
255    );
256
257    /**
258     * Run a repeating asynchronous task. This will never run on the
259     * server thread
260     *
261     * @param runnable Task to run
262     * @param taskTime Task interval
263     * @return Created task object, can be used to cancel the task
264     */
265    public abstract PlotSquaredTask taskRepeatAsync(
266            @NonNull Runnable runnable,
267            @NonNull TaskTime taskTime
268    );
269
270    /**
271     * Run an asynchronous task. This will never run on the server thread
272     *
273     * @param runnable Task to run
274     */
275    public abstract void taskAsync(@NonNull Runnable runnable);
276
277    /**
278     * Run a synchronous task. If using a platform scheduler, this is guaranteed
279     * to run on the server thread
280     *
281     * @param runnable Task to run
282     */
283    public abstract void task(@NonNull Runnable runnable);
284
285    /**
286     * Run a synchronous task after a given delay.
287     * If using a platform scheduler, this is guaranteed to run on the server thread
288     *
289     * @param runnable Task to run
290     * @param taskTime Task delay
291     */
292    public abstract void taskLater(@NonNull Runnable runnable, @NonNull TaskTime taskTime);
293
294    /**
295     * Run an asynchronous task after a given delay. This will never
296     * run on the server thread
297     *
298     * @param runnable Task to run
299     * @param taskTime Task delay
300     */
301    public abstract void taskLaterAsync(@NonNull Runnable runnable, @NonNull TaskTime taskTime);
302
303}