Skip to content

Commit 6e19792

Browse files
committed
Add some utilities.
1 parent 4ca4861 commit 6e19792

File tree

5 files changed

+271
-0
lines changed

5 files changed

+271
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
package com.eternalcode.commons.bukkit.runnable;
2+
3+
import java.util.Collection;
4+
import java.util.function.Consumer;
5+
import org.bukkit.Bukkit;
6+
import org.bukkit.entity.Player;
7+
8+
public abstract class OnlinePlayersRunnable implements Runnable {
9+
10+
public static OnlinePlayersRunnable consuming(Consumer<Player> consumer) {
11+
if (consumer == null) {
12+
throw new IllegalArgumentException("Consumer cannot be null");
13+
}
14+
15+
return new OnlinePlayersRunnable() {
16+
@Override
17+
public void runFor(Player player) {
18+
consumer.accept(player);
19+
}
20+
};
21+
}
22+
23+
public static Runnable asRunnable(Consumer<Player> consumer) {
24+
return consuming(consumer);
25+
}
26+
27+
@Override
28+
public final void run() {
29+
Collection<? extends Player> onlinePlayers = Bukkit.getOnlinePlayers();
30+
31+
if (onlinePlayers.isEmpty()) {
32+
return;
33+
}
34+
35+
for (Player player : onlinePlayers) {
36+
if (player != null && player.isOnline()) {
37+
try {
38+
runFor(player);
39+
}
40+
catch (Exception exception) {
41+
handlePlayerException(player, exception);
42+
}
43+
}
44+
}
45+
}
46+
47+
protected void handlePlayerException(Player player, Exception exception) {
48+
Bukkit.getLogger().warning(
49+
String.format(
50+
"Error processing player %s: %s",
51+
player.getName(), exception.getMessage())
52+
);
53+
}
54+
55+
public abstract void runFor(Player player);
56+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
package com.eternalcode.commons;
2+
3+
/*
4+
* Copyright (c) 2021 dzikoysk
5+
*
6+
* Licensed under the Apache License, Version 2.0 (the "License");
7+
* you may not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
19+
import java.util.function.Function;
20+
21+
@FunctionalInterface
22+
public interface ThrowingFunction<T, R, E extends Throwable> {
23+
24+
static <T, S extends Exception> ThrowingFunction<T, T, S> identity() {
25+
return t -> t;
26+
}
27+
/**
28+
* Map throwing function in default stream api with exception consume support
29+
*
30+
* @param function the function to apply
31+
* @param exceptionHandler the exception consumer
32+
* @param <T> type of function parameter
33+
* @param <R> type of function result
34+
* @param <E> type of exception
35+
* @return a new function
36+
*/
37+
@SuppressWarnings("unchecked")
38+
static <T, R, E extends Throwable> Function<T, R> asFunction(
39+
ThrowingFunction<T, R, E> function,
40+
Function<E, R> exceptionHandler) {
41+
return value -> {
42+
try {
43+
return function.apply(value);
44+
}
45+
catch (Throwable exception) {
46+
return exceptionHandler.apply((E) exception);
47+
}
48+
};
49+
}
50+
R apply(T t) throws E;
51+
}
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
package com.eternalcode.commons.algorithm;
2+
3+
import java.util.ArrayList;
4+
import java.util.Collection;
5+
import java.util.List;
6+
import java.util.function.Consumer;
7+
8+
/**
9+
* Utility for processing collections in batches.
10+
*
11+
* @see <a href="https://en.wikipedia.org/wiki/Batch_processing">Batch processing - Wikipedia</a>
12+
*/
13+
public class BatchProcessor<T> {
14+
15+
private final List<T> elements;
16+
private final int batchSize;
17+
private int position;
18+
19+
/**
20+
* Creates a new batch processor.
21+
*
22+
* @param elements collection to process
23+
* @param batchSize size of each batch (must be > 0)
24+
*/
25+
public BatchProcessor(Collection<T> elements, int batchSize) {
26+
if (batchSize <= 0) {
27+
throw new IllegalArgumentException("Batch size must be greater than 0");
28+
}
29+
30+
this.elements = new ArrayList<>(elements);
31+
this.batchSize = batchSize;
32+
this.position = 0;
33+
}
34+
35+
/**
36+
* Processes the next batch of elements.
37+
*
38+
* @param processor action to apply to each element
39+
* @return true if more elements remain, false if all processed
40+
*/
41+
public boolean processNext(Consumer<T> processor) {
42+
if (position >= elements.size()) {
43+
return false;
44+
}
45+
46+
int endPosition = Math.min(position + batchSize, elements.size());
47+
List<T> currentBatch = elements.subList(position, endPosition);
48+
49+
currentBatch.forEach(processor);
50+
51+
position = endPosition;
52+
return position < elements.size();
53+
}
54+
55+
/**
56+
* Resets processor to start from beginning.
57+
*/
58+
public void reset() {
59+
position = 0;
60+
}
61+
62+
public int getBatchSize() {
63+
return batchSize;
64+
}
65+
66+
public int getPosition() {
67+
return position;
68+
}
69+
70+
public int getTotalSize() {
71+
return elements.size();
72+
}
73+
74+
public boolean isComplete() {
75+
return position >= elements.size();
76+
}
77+
78+
public int getRemainingCount() {
79+
return Math.max(0, elements.size() - position);
80+
}
81+
82+
public int getProcessedBatchCount() {
83+
return (position + batchSize - 1) / batchSize;
84+
}
85+
86+
public int getTotalBatchCount() {
87+
return (elements.size() + batchSize - 1) / batchSize;
88+
}
89+
90+
public double getProgress() {
91+
if (elements.isEmpty()) {
92+
return 1.0;
93+
}
94+
return (double) position / elements.size();
95+
}
96+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package com.eternalcode.commons.concurrent;
2+
3+
import java.util.function.BiConsumer;
4+
import java.util.function.Consumer;
5+
import java.util.logging.Level;
6+
import java.util.logging.Logger;
7+
8+
public final class FutureHandler<T> implements BiConsumer<T, Throwable> {
9+
10+
private static final Logger LOGGER = Logger.getLogger(FutureHandler.class.getName());
11+
12+
private final Consumer<T> success;
13+
14+
private FutureHandler(Consumer<T> success) {
15+
this.success = success;
16+
}
17+
18+
public static <T> FutureHandler<T> whenSuccess(Consumer<T> success) {
19+
return new FutureHandler<>(success);
20+
}
21+
22+
public static <T> T handleException(Throwable cause) {
23+
LOGGER.log(
24+
Level.SEVERE,
25+
String.format("Caught an exception in future execution: %s", cause.getMessage()),
26+
cause);
27+
return null;
28+
}
29+
30+
@Override
31+
public void accept(T value, Throwable cause) {
32+
if (cause != null) {
33+
LOGGER.log(Level.SEVERE, "Caught an exception in future execution: " + cause.getMessage(), cause);
34+
return;
35+
}
36+
37+
success.accept(value);
38+
}
39+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package com.eternalcode.commons.time;
2+
3+
import java.time.Duration;
4+
5+
/**
6+
* Utility for converting Duration to Minecraft server ticks.
7+
*
8+
* @see <a href="https://minecraft.wiki/w/Tick">Tick - Minecraft Wiki</a>
9+
*/
10+
public final class DurationTickUtil {
11+
12+
private static final int TICK_DURATION_MILLIS = 50;
13+
14+
private DurationTickUtil() {
15+
throw new UnsupportedOperationException("Utility class");
16+
}
17+
18+
/**
19+
* Converts Duration to Minecraft server ticks.
20+
* One tick equals 50 milliseconds (20 TPS).
21+
*
22+
* @param duration the duration to convert
23+
* @return number of ticks
24+
* @throws ArithmeticException if the result overflows an int
25+
*/
26+
public static int durationToTicks(Duration duration) {
27+
return Math.toIntExact(duration.toMillis() / TICK_DURATION_MILLIS);
28+
}
29+
}

0 commit comments

Comments
 (0)