![JAR search and dependency download from the Maven repository](/logo.png)
io.github.dailystruggle.rtp.common.selection.region.Region Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of RTP Show documentation
Show all versions of RTP Show documentation
a random teleport plugin
package io.github.dailystruggle.rtp.common.selection.region;
import io.github.dailystruggle.commandsapi.common.CommandsAPI;
import io.github.dailystruggle.rtp.common.RTP;
import io.github.dailystruggle.rtp.common.configuration.ConfigParser;
import io.github.dailystruggle.rtp.common.configuration.enums.*;
import io.github.dailystruggle.rtp.common.factory.Factory;
import io.github.dailystruggle.rtp.common.factory.FactoryValue;
import io.github.dailystruggle.rtp.common.playerData.TeleportData;
import io.github.dailystruggle.rtp.common.selection.region.selectors.memory.shapes.MemoryShape;
import io.github.dailystruggle.rtp.common.selection.region.selectors.shapes.Shape;
import io.github.dailystruggle.rtp.common.selection.region.selectors.verticalAdjustors.VerticalAdjustor;
import io.github.dailystruggle.rtp.common.selection.worldborder.WorldBorder;
import io.github.dailystruggle.rtp.common.serverSide.substitutions.*;
import io.github.dailystruggle.rtp.common.tasks.FillTask;
import io.github.dailystruggle.rtp.common.tasks.RTPRunnable;
import io.github.dailystruggle.rtp.common.tasks.RTPTaskPipe;
import io.github.dailystruggle.rtp.common.tasks.teleport.LoadChunks;
import org.jetbrains.annotations.Nullable;
import org.simpleyaml.configuration.MemorySection;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.BiConsumer;
import java.util.function.Predicate;
import java.util.logging.Level;
import java.util.stream.Collectors;
public class Region extends FactoryValue {
public static final List> onPlayerQueuePush = new ArrayList<>();
public static final List> onPlayerQueuePop = new ArrayList<>();
//semaphore needed in case of async usage
//storage for region verifiers to use for ALL regions
private static final Semaphore regionVerifiersLock = new Semaphore(1);
private static final List> regionVerifiers = new ArrayList<>();
private static final Set unsafeBlocks = new ConcurrentSkipListSet<>();
private static final AtomicLong lastUpdate = new AtomicLong(0);
private static final AtomicInteger safetyRadius = new AtomicInteger(0);
public static int maxBiomeChecksPerGen = 100;
private final Semaphore cacheGuard = new Semaphore(1);
/**
* public/shared cache for this region
*/
public ConcurrentLinkedQueue> locationQueue = new ConcurrentLinkedQueue<>();
public ConcurrentHashMap locAssChunks = new ConcurrentHashMap<>();
/**
* When reserving/recycling locations for specific players,
* I want to guard against
*/
public ConcurrentHashMap>> perPlayerLocationQueue = new ConcurrentHashMap<>();
/**
*
*/
public ConcurrentHashMap>> fastLocations = new ConcurrentHashMap<>();
public RTPTaskPipe cachePipeline = new RTPTaskPipe();
public RTPTaskPipe miscPipeline = new RTPTaskPipe();
protected ConcurrentLinkedQueue playerQueue = new ConcurrentLinkedQueue<>();
public Region(String name, EnumMap params) {
super(RegionKeys.class, name);
this.name = name;
this.data.putAll(params);
ConfigParser logging = (ConfigParser) RTP.configs.getParser(LoggingKeys.class);
boolean detailed_region_init = true;
if (logging != null) {
Object o = logging.getConfigValue(LoggingKeys.detailed_region_init, false);
if (o instanceof Boolean) {
detailed_region_init = (Boolean) o;
} else {
detailed_region_init = Boolean.parseBoolean(o.toString());
}
}
Object shape = getShape();
Object world = params.get(RegionKeys.world);
String worldName;
if (world instanceof RTPWorld) worldName = ((RTPWorld) world).name();
else {
worldName = String.valueOf(world);
}
if (shape instanceof MemoryShape>) {
if (detailed_region_init) {
RTP.log(Level.INFO, "&00FFFF[RTP] [" + name + "] memory shape detected, reading location data from file...");
}
((MemoryShape>) shape).load(name + ".yml", worldName);
long iter = ((MemoryShape>) shape).fillIter.get();
if (iter > 0 && iter < Double.valueOf(((MemoryShape>) shape).getRange()).longValue())
RTP.getInstance().fillTasks.put(name, new FillTask(this, iter));
}
long cacheCap = getNumber(RegionKeys.cacheCap, 10L).longValue();
for (long i = cachePipeline.size(); i < cacheCap; i++) {
cachePipeline.add(new Cache());
}
}
/**
* addGlobalRegionVerifier - add a region verifier to use for ALL regions
*
* @param locationCheck verifier method to reference.
* param: world name, 3D point
* return: boolean - true on good location, false on bad location
*/
public static void addGlobalRegionVerifier(Predicate locationCheck) {
try {
regionVerifiersLock.acquire();
} catch (InterruptedException e) {
regionVerifiersLock.release();
return;
}
regionVerifiers.add(locationCheck);
regionVerifiersLock.release();
}
public static void clearGlobalRegionVerifiers() {
try {
regionVerifiersLock.acquire();
} catch (InterruptedException e) {
regionVerifiersLock.release();
return;
}
regionVerifiers.clear();
regionVerifiersLock.release();
}
public static boolean checkGlobalRegionVerifiers(RTPLocation location) {
try {
regionVerifiersLock.acquire();
} catch (InterruptedException e) {
regionVerifiersLock.release();
return false;
}
for (Predicate verifier : regionVerifiers) {
try {
//if invalid placement, stop and return invalid
//clone location to prevent methods from messing with the data
if (!verifier.test(location)) {
regionVerifiersLock.release();
return false;
}
} catch (Throwable throwable) {
throwable.printStackTrace();
}
}
regionVerifiersLock.release();
return true;
}
public void execute(long availableTime) {
long start = System.nanoTime();
miscPipeline.execute(availableTime);
long cacheCap = getNumber(RegionKeys.cacheCap, 10L).longValue();
cacheCap = Math.max(cacheCap, playerQueue.size());
try {
cacheGuard.acquire();
if (locationQueue.size() >= cacheCap) return;
while (cachePipeline.size() + locationQueue.size() < cacheCap + playerQueue.size())
cachePipeline.add(new Cache());
cachePipeline.execute(availableTime - (System.nanoTime() - start)); //todo: too fast for server
// cachePipeline.execute(0);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
cacheGuard.release();
}
while (locationQueue.size() > 0 && playerQueue.size() > 0) {
UUID playerId = playerQueue.poll();
TeleportData teleportData = RTP.getInstance().latestTeleportData.get(playerId);
if (teleportData == null || teleportData.completed) {
RTP.getInstance().processingPlayers.remove(playerId);
return;
}
RTPPlayer player = RTP.serverAccessor.getPlayer(playerId);
if (player == null) continue;
Map.Entry pair = locationQueue.poll();
if (pair == null) {
playerQueue.add(playerId);
continue;
}
teleportData.attempts = pair.getValue();
RTPCommandSender sender = RTP.serverAccessor.getSender(CommandsAPI.serverId);
LoadChunks loadChunks = new LoadChunks(sender, player, pair.getKey(), this);
teleportData.nextTask = loadChunks;
RTP.getInstance().latestTeleportData.put(playerId, teleportData);
RTP.getInstance().loadChunksPipeline.add(loadChunks);
onPlayerQueuePop.forEach(consumer -> consumer.accept(this, playerId));
Iterator iterator = playerQueue.iterator();
int i = 0;
while (iterator.hasNext()) {
UUID id = iterator.next();
++i;
TeleportData data = RTP.getInstance().latestTeleportData.get(id);
RTP.getInstance().processingPlayers.add(id);
if (data == null) {
data = new TeleportData();
data.completed = false;
data.sender = RTP.serverAccessor.getSender(CommandsAPI.serverId);
data.time = System.currentTimeMillis();
data.delay = sender.delay();
data.targetRegion = this;
data.originalLocation = player.getLocation();
RTP.getInstance().latestTeleportData.put(id, data);
}
data.queueLocation = i;
RTP.serverAccessor.sendMessage(id, MessagesKeys.queueUpdate);
}
}
}
public boolean hasLocation(@Nullable UUID uuid) {
boolean res = locationQueue.size() > 0;
res |= (uuid != null) && (perPlayerLocationQueue.containsKey(uuid));
return res;
}
public Map.Entry getLocation(RTPCommandSender sender, RTPPlayer player, @Nullable Set biomeNames) {
Map.Entry pair = null;
UUID playerId = player.uuid();
boolean custom = biomeNames != null && biomeNames.size() > 0;
if (!custom && perPlayerLocationQueue.containsKey(playerId)) {
ConcurrentLinkedQueue> playerLocationQueue = perPlayerLocationQueue.get(playerId);
RTPChunk chunk = null;
while (playerLocationQueue.size() > 0) {
if (chunk != null) chunk.unload();
pair = playerLocationQueue.poll();
if (pair == null || pair.getKey() == null) continue;
RTPLocation left = pair.getKey();
boolean pass = true;
int cx = (left.x() > 0) ? left.x() / 16 : left.x() / 16 - 1;
int cz = (left.z() > 0) ? left.z() / 16 : left.z() / 16 - 1;
CompletableFuture chunkAt = left.world().getChunkAt(cx, cz);
try {
chunk = chunkAt.get();
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
continue;
}
if(chunk == null) return null;
long t = System.currentTimeMillis();
long dt = t - lastUpdate.get();
if (dt > 5000 || dt < 0) {
ConfigParser safety = (ConfigParser) RTP.configs.getParser(SafetyKeys.class);
Object value = safety.getConfigValue(SafetyKeys.unsafeBlocks, new ArrayList<>());
unsafeBlocks.clear();
if (value instanceof Collection) {
unsafeBlocks.addAll(((Collection>) value).stream().filter(Objects::nonNull).map(Object::toString).collect(Collectors.toSet()));
}
lastUpdate.set(t);
safetyRadius.set(Math.max(safety.getNumber(SafetyKeys.safetyRadius, 0).intValue(), 7));
}
//todo: waterlogged check
int safe = safetyRadius.get();
RTPBlock block;
RTPChunk chunk1;
Map,RTPChunk> chunks = new HashMap<>();
chunks.put(Arrays.asList(chunk.x(), chunk.z()), chunk);
chunk.keep(true);
for (int x = left.x() - safe; x < left.x() + safe && pass; x++) {
int xx = x;
int dx = Math.abs(xx/16);
int chunkX = chunk.x();
if(xx < 0) {
chunkX-=dx+1;
if(xx%16==0) xx+=16*dx;
else xx+=16*(dx+1);
} else if(xx >= 16) {
chunkX+=dx;
xx-=16*dx;
}
for (int z = left.z() - safe; z < left.z() + safe && pass; z++) {
int zz = z;
int dz = Math.abs(zz/16);
int chunkZ = chunk.z();
if(zz < 0) {
chunkZ-=dz+1;
if(zz%16==0) zz+=16*dz;
else zz+=16*(dz+1);
} else if(zz >= 16) {
chunkZ+=dz;
zz-=16*dz;
}
List xz = Arrays.asList(chunkX, chunkZ);
if(chunks.containsKey(xz)) chunk1 = chunks.get(xz);
else {
try {
chunk1 = getWorld().getChunkAt(chunkX, chunkZ).get();
chunks.put(xz,chunk1);
chunk1.keep(true);
} catch (InterruptedException | ExecutionException e) {
return null;
}
}
for (int y = left.y() - safe; y < left.y() + safe && pass; y++) {
if(y>getWorld().getMaxHeight() || y 0) {
pair = locationQueue.poll();
if (pair == null) return null;
RTPLocation left = pair.getKey();
if (left == null) return pair;
boolean pass = checkGlobalRegionVerifiers(left);
if (pass) return pair;
}
if (custom || sender.hasPermission("rtp.unqueued")) {
pair = getLocation(biomeNames);
long attempts = pair.getValue();
TeleportData data = RTP.getInstance().latestTeleportData.get(playerId);
if (data != null && !data.completed) {
data.attempts = attempts;
}
} else {
RTP.getInstance().processingPlayers.add(playerId);
TeleportData data = RTP.getInstance().latestTeleportData.get(playerId);
if (data == null) {
data = new TeleportData();
data.sender = (sender != null) ? sender : player;
data.completed = false;
data.time = System.currentTimeMillis();
data.delay = sender.delay();
data.targetRegion = this;
data.originalLocation = player.getLocation();
RTP.getInstance().latestTeleportData.put(playerId, data);
}
onPlayerQueuePush.forEach(consumer -> consumer.accept(this, playerId));
playerQueue.add(playerId);
data.queueLocation = playerQueue.size();
RTP.serverAccessor.sendMessage(playerId, MessagesKeys.queueUpdate);
}
return pair;
}
protected enum FailTypes {
biome,
worldBorder,
timeout,
vert,
safety,
safetyExternal,
misc
}
@Nullable
public Map.Entry getLocation(@Nullable Set biomeNames) {
boolean defaultBiomes = false;
ConfigParser performance = (ConfigParser) RTP.configs.getParser(PerformanceKeys.class);
ConfigParser safety = (ConfigParser) RTP.configs.getParser(SafetyKeys.class);
ConfigParser logging = (ConfigParser) RTP.configs.getParser(LoggingKeys.class);
Object o;
if (biomeNames == null || biomeNames.size() == 0) {
defaultBiomes = true;
o = safety.getConfigValue(SafetyKeys.biomeWhitelist, false);
boolean whitelist = (o instanceof Boolean) ? (Boolean) o : Boolean.parseBoolean(o.toString());
o = safety.getConfigValue(SafetyKeys.biomes, null);
List biomeList = (o instanceof List) ? ((List>) o).stream().map(Object::toString).collect(Collectors.toList()) : null;
Set biomeSet = (biomeList == null)
? new HashSet<>()
: biomeList.stream().map(String::toUpperCase).collect(Collectors.toSet());
if (whitelist) {
biomeNames = biomeSet;
} else {
Set biomes = RTP.serverAccessor.getBiomes(getWorld());
Set set = new HashSet<>();
for (String s : biomes) {
if (!biomeSet.contains(s.toUpperCase())) {
set.add(s);
}
}
biomeNames = set;
}
}
boolean verbose = false;
if (logging != null) {
o = logging.getConfigValue(LoggingKeys.selection_failure, false);
if (o instanceof Boolean) {
verbose = (Boolean) o;
} else {
verbose = Boolean.parseBoolean(o.toString());
}
}
Shape> shape = getShape();
if (shape == null) return null;
VerticalAdjustor> vert = getVert();
if (vert == null) return null;
o = safety.getConfigValue(SafetyKeys.unsafeBlocks, new ArrayList<>());
Set unsafeBlocks = (o instanceof Collection) ? ((Collection>) o)
.stream().map(o1 -> o1.toString().toUpperCase()).collect(Collectors.toSet())
: new HashSet<>();
int safetyRadius = safety.getNumber(SafetyKeys.safetyRadius, 0).intValue();
long maxAttemptsBase = performance.getNumber(PerformanceKeys.maxAttempts, 20).longValue();
maxAttemptsBase = Math.max(maxAttemptsBase, 1);
long maxAttempts = maxAttemptsBase;
long maxBiomeChecks = maxBiomeChecksPerGen * maxAttempts;
if(!defaultBiomes) maxBiomeChecks *= 10;
long biomeChecks = 0L;
RTPWorld world = getWorld();
Map> failMap = new EnumMap<>(FailTypes.class);
for(FailTypes f : FailTypes.values()) failMap.put(f,new HashMap<>());
List> selections = new ArrayList<>();
RTPLocation location = null;
long i = 1;
boolean biomeRecall = Boolean.parseBoolean(performance.getConfigValue(PerformanceKeys.biomeRecall, false).toString());
for (; i <= maxAttempts; i++) {
long l = -1;
int[] select;
if (shape instanceof MemoryShape) {
MemoryShape> memoryShape = (MemoryShape>) shape;
if (biomeRecall && !defaultBiomes) {
List> biomes = new ArrayList<>();
for (String biomeName : biomeNames) {
ConcurrentSkipListMap map = memoryShape.biomeLocations.get(biomeName);
if (map != null) {
biomes.addAll(map.entrySet());
}
}
// RTP.log(Level.CONFIG,"biome input - " + biomes);
Map.Entry entry;
if(biomes.size()>0) {
int nextInt = ThreadLocalRandom.current().nextInt(biomes.size());
entry = biomes.get(nextInt);
// RTP.log(Level.CONFIG,"target selected - " + entry.toString());
l = entry.getKey() + ThreadLocalRandom.current().nextLong(entry.getValue());
// RTP.log(Level.CONFIG,"final selection - " + l);
}
else l = memoryShape.rand();
} else {
l = memoryShape.rand();
}
select = memoryShape.locationToXZ(l);
// if (verbose) selections.add(new AbstractMap.SimpleEntry<>((long) selections.size(), l));
} else {
select = shape.select();
// if (verbose) selections.add(new AbstractMap.SimpleEntry<>((long) select[0], (long) select[1]));
}
String currBiome = world.getBiome(select[0] * 16 + 7, (vert.minY() + vert.maxY()) / 2, select[1] * 16 + 7);
for (; biomeChecks < maxBiomeChecks && !biomeNames.contains(currBiome); biomeChecks++, maxAttempts++, i++) {
if (shape instanceof MemoryShape) {
MemoryShape> memoryShape = (MemoryShape>) shape;
if (defaultBiomes && biomeRecall) {
memoryShape.addBadLocation(l);
}
if (biomeRecall && !defaultBiomes) {
List> biomes = new ArrayList<>();
for (String biomeName : biomeNames) {
ConcurrentSkipListMap map = memoryShape.biomeLocations.get(biomeName);
if (map != null) {
biomes.addAll(map.entrySet());
}
}
Map.Entry entry;
if(biomes.size()>0) {
int nextInt = ThreadLocalRandom.current().nextInt(biomes.size());
entry = biomes.get(nextInt);
l = entry.getKey() + ThreadLocalRandom.current().nextLong(entry.getValue());
}
else l = memoryShape.rand();
// RTP.log(Level.WARNING,"A");
} else {
l = memoryShape.rand();
// RTP.log(Level.WARNING,"B");
}
select = memoryShape.locationToXZ(l);
} else {
select = shape.select();
// if (verbose) selections.add(new AbstractMap.SimpleEntry<>((long) select[0], (long) select[1]));
}
String key = "biome="+currBiome;
if(verbose) {
failMap.get(FailTypes.biome).compute(key, (s, aLong) -> {
if (aLong == null) return 1L;
return ++aLong;
});
}
currBiome = world.getBiome(select[0] * 16 + 7, (vert.minY() + vert.maxY()) / 2, select[1] * 16 + 7);
}
if (biomeChecks >= maxBiomeChecks) return new AbstractMap.SimpleEntry<>(null, i);
if (verbose) {
if(shape instanceof MemoryShape) selections.add(new AbstractMap.SimpleEntry<>((long) selections.size(), l));
else selections.add(new AbstractMap.SimpleEntry<>((long) select[0], (long) select[1]));
}
WorldBorder border = RTP.serverAccessor.getWorldBorder(world.name());
if (!border.isInside().apply(new RTPLocation(world, select[0] * 16, (vert.maxY() + vert.minY()) / 2, select[1] * 16))) {
maxAttempts++;
Long worldBorderFails = failMap.get(FailTypes.worldBorder).getOrDefault("OUTSIDE_BORDER", 0L);
worldBorderFails++;
if (worldBorderFails > 1000) {
new IllegalStateException("1000 worldborder checks failed. region/selection is likely outside the worldborder").printStackTrace();
return new AbstractMap.SimpleEntry<>(null, i);
}
failMap.get(FailTypes.worldBorder).put("OUTSIDE_BORDER",worldBorderFails);
continue;
}
CompletableFuture cfChunk = world.getChunkAt(select[0], select[1]);
RTP.futures.add(cfChunk);
RTPChunk chunk;
try {
chunk = cfChunk.get();
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
return new AbstractMap.SimpleEntry<>(null, i);
}
if(chunk == null) return null;
location = vert.adjust(chunk);
if (location == null) {
if (defaultBiomes && shape instanceof MemoryShape && biomeRecall) {
((MemoryShape>) shape).addBadLocation(l);
}
if(verbose) {
failMap.get(FailTypes.vert).compute("biome=" + currBiome,
(s, aLong) -> (aLong==null) ? (1L) : (++aLong));
}
chunk.unload();
continue;
}
currBiome = world.getBiome(location.x(), location.y(), location.z());
if (!biomeNames.contains(currBiome)) {
biomeChecks++;
maxAttempts++;
if(defaultBiomes && shape instanceof MemoryShape && biomeRecall) {
((MemoryShape>) shape).addBadLocation(l);
}
if(verbose) {
failMap.get(FailTypes.biome).compute("biome=" + currBiome,
(s, aLong) -> (aLong == null) ? 1L : ++aLong);
}
chunk.unload();
continue;
}
boolean pass = true;
//todo: waterlogged check
RTPBlock block;
RTPChunk chunk1;
Map,RTPChunk> chunks = new HashMap<>();
chunks.put(Arrays.asList(chunk.x(),chunk.z()),chunk);
chunk.keep(true);
for (int x = location.x() - safetyRadius; x < location.x() + safetyRadius && pass; x++) {
int xx = x;
int dx = Math.abs(xx/16);
int chunkX = chunk.x();
if(xx < 0) {
chunkX-=dx+1;
if(xx%16==0) xx+=16*dx;
else xx+=16*(dx+1);
} else if(xx >= 16) {
chunkX+=dx;
xx-=16*dx;
}
for (int z = location.z() - safetyRadius; z < location.z() + safetyRadius && pass; z++) {
int zz = z;
int dz = Math.abs(zz/16);
int chunkZ = chunk.z();
if(zz < 0) {
chunkZ-=dz+1;
if(zz%16==0) zz+=16*dz;
else zz+=16*(dz+1);
} else if(zz >= 16) {
chunkZ+=dz;
zz-=16*dz;
}
List xz = Arrays.asList(chunkX, chunkZ);
if(chunks.containsKey(xz)) chunk1 = chunks.get(xz);
else {
try {
chunk1 = getWorld().getChunkAt(chunkX, chunkZ).get();
chunks.put(xz,chunk1);
chunk1.keep(true);
} catch (InterruptedException | ExecutionException e) {
return null;
}
}
for (int y = location.y() - safetyRadius; y < location.y() + safetyRadius && pass; y++) {
if(y>getWorld().getMaxHeight() || y {
if (aLong == null) return 1L;
return ++aLong;
});
}
}
}
}
}
for(RTPChunk usedChunk : chunks.values()) usedChunk.keep(false);
pass &= checkGlobalRegionVerifiers(location);
if (pass) {
if (shape instanceof MemoryShape) {
if(l>0) ((MemoryShape>) shape).addBiomeLocation(l, currBiome);
}
break;
} else {
if (verbose) failMap.get(FailTypes.misc).compute("location="+"("+location.x()+","+location.y()+","+location.z(),
(s, aLong) -> (aLong==null) ? 1L : ++aLong);
if (shape instanceof MemoryShape) {
((MemoryShape>) shape).addBadLocation(l);
}
location = null;
}
chunk.unload();
}
// if (verbose) {
if (verbose && i >= maxAttempts || i > maxAttemptsBase*maxBiomeChecksPerGen) {
RTP.log(Level.INFO, "#00ff80[RTP] [" + name + "] failed to generate a location within " + maxAttempts + " tries. Adjust your configuration.");
for(Map.Entry> mapEntry : failMap.entrySet()) {
Map map = mapEntry.getValue();
String[] output = new String[map.size()];
int pos = 0;
long count = 0;
for(Map.Entry entry : map.entrySet()) {
output[pos] = "#00ff80[RTP] [" + name + "] " + " cause=" + mapEntry.getKey() + " " + entry.getKey() + " fails=" + entry.getValue();
count+=entry.getValue();
pos++;
}
RTP.log(Level.INFO,"#00ff80[RTP] [" + name + "] " + " cause=" + mapEntry.getKey() + " fails=" + count);
for(String out : output) {
RTP.log(Level.INFO,out);
}
}
StringBuilder selectionsStr = new StringBuilder();
boolean first = true;
selectionsStr = selectionsStr.append("{");
for(Map.Entry entry : selections) {
if(!first) {
selectionsStr = selectionsStr.append(",");
}
selectionsStr = selectionsStr.append("(").append(entry.getKey()).append(",").append(entry.getValue()).append(")");
}
selectionsStr = selectionsStr.append("}");
RTP.log(Level.INFO, "#0f0080[RTP] [" + name + "] selections: " + selectionsStr);
}
i = Math.min(i, maxAttempts);
return new AbstractMap.SimpleEntry<>(location, i);
}
public void shutDown() {
Shape> shape = getShape();
if (shape == null) return;
RTPWorld world = getWorld();
if (world == null) return;
if (shape instanceof MemoryShape>) {
((MemoryShape>) shape).save(this.name + ".yml", world.name());
}
cachePipeline.stop();
cachePipeline.clear();
playerQueue.clear();
perPlayerLocationQueue.clear();
fastLocations.clear();
locationQueue.clear();
locAssChunks.forEach((rtpLocation, chunkSet) -> chunkSet.keep(false));
locAssChunks.clear();
}
@Override
public Region clone() {
Region clone = (Region) super.clone();
clone.data = data.clone();
clone.locationQueue = new ConcurrentLinkedQueue<>();
clone.locAssChunks = new ConcurrentHashMap<>();
clone.playerQueue = new ConcurrentLinkedQueue<>();
clone.perPlayerLocationQueue = new ConcurrentHashMap<>();
clone.fastLocations = new ConcurrentHashMap<>();
return clone;
}
public Map params() {
Map res = new ConcurrentHashMap<>();
for (Map.Entry extends Enum>, ?> e : data.entrySet()) {
Object value = e.getValue();
if (value instanceof RTPWorld) {
res.put("world", ((RTPWorld) value).name());
} else if (value instanceof Shape) {
res.put("shape", ((Shape>) value).name);
EnumMap extends Enum>, Object> data = ((Shape>) value).getData();
for (Map.Entry extends Enum>, ?> dataEntry : data.entrySet()) {
res.put(dataEntry.getKey().name(), dataEntry.getValue().toString());
}
} else if (value instanceof VerticalAdjustor) {
res.put("vert", ((VerticalAdjustor>) value).name);
EnumMap extends Enum>, Object> data = ((VerticalAdjustor>) value).getData();
for (Map.Entry extends Enum>, ?> dataEntry : data.entrySet()) {
res.put(dataEntry.getKey().name(), dataEntry.getValue().toString());
}
} else if (value instanceof String)
res.put(e.getKey().name(), (String) value);
else {
res.put(e.getKey().name(), value.toString());
}
}
return res;
}
public ChunkSet chunks(RTPLocation location, long radius) {
long sz = (radius * 2 + 1) * (radius * 2 + 1);
if (locAssChunks.containsKey(location)) {
ChunkSet chunkSet = locAssChunks.get(location);
if (chunkSet.chunks.size() >= sz) return chunkSet;
chunkSet.keep(false);
locAssChunks.remove(location);
}
int cx = location.x();
int cz = location.z();
cx = (cx > 0) ? cx / 16 : cx / 16 - 1;
cz = (cz > 0) ? cz / 16 : cz / 16 - 1;
List> chunks = new ArrayList<>();
Shape> shape = getShape();
if (shape == null) return null;
VerticalAdjustor> vert = getVert();
if (vert == null) return null;
RTPWorld rtpWorld = getWorld();
if (rtpWorld == null) return null;
for (long i = -radius; i <= radius; i++) {
for (long j = -radius; j <= radius; j++) {
CompletableFuture cfChunk = location.world().getChunkAt((int) (cx + i), (int) (cz + j));
chunks.add(cfChunk);
}
}
ChunkSet chunkSet = new ChunkSet(chunks, new CompletableFuture<>());
chunkSet.keep(true);
locAssChunks.put(location, chunkSet);
return chunkSet;
}
public void removeChunks(RTPLocation location) {
if (!locAssChunks.containsKey(location)) return;
ChunkSet chunkSet = locAssChunks.get(location);
chunkSet.keep(false);
locAssChunks.remove(location);
}
public CompletableFuture> fastQueue(UUID id) {
if (fastLocations.containsKey(id)) return fastLocations.get(id);
CompletableFuture> res = new CompletableFuture<>();
fastLocations.put(id, res);
miscPipeline.add(new Cache(id));
return res;
}
public void queue(UUID id) {
perPlayerLocationQueue.putIfAbsent(id, new ConcurrentLinkedQueue<>());
miscPipeline.add(new Cache(id));
}
public long getTotalQueueLength(UUID uuid) {
long res = locationQueue.size();
ConcurrentLinkedQueue> queue = perPlayerLocationQueue.get(uuid);
if (queue != null) res += queue.size();
if (fastLocations.containsKey(uuid)) res++;
return res;
}
public long getPublicQueueLength() {
return locationQueue.size();
}
public long getPersonalQueueLength(UUID uuid) {
long res = 0;
ConcurrentLinkedQueue> queue = perPlayerLocationQueue.get(uuid);
if (queue != null) res += queue.size();
if (fastLocations.containsKey(uuid)) res++;
return res;
}
public Shape> getShape() {
boolean wbo = false;
Object o = data.getOrDefault(RegionKeys.worldBorderOverride, false);
if (o instanceof Boolean) wbo = (Boolean) o;
else if (o instanceof String) {
wbo = Boolean.parseBoolean((String) o);
data.put(RegionKeys.worldBorderOverride, wbo);
}
RTPWorld world;
o = data.get(RegionKeys.world);
if (o instanceof RTPWorld) world = (RTPWorld) o;
else if (o instanceof String) {
world = RTP.serverAccessor.getRTPWorld((String) o);
} else world = null;
if (world == null) world = RTP.serverAccessor.getRTPWorlds().get(0);
Object shapeObj = data.get(RegionKeys.shape);
Shape> shape;
if (shapeObj instanceof Shape) {
shape = (Shape>) shapeObj;
} else if (shapeObj instanceof MemorySection) {
final Map shapeMap = ((MemorySection) shapeObj).getMapValues(true);
String shapeName = String.valueOf(shapeMap.get("name"));
Factory> factory = (Factory>) RTP.factoryMap.get(RTP.factoryNames.shape);
shape = (Shape>) factory.get(shapeName);
EnumMap, Object> shapeData = shape.getData();
for (Map.Entry extends Enum>, Object> e : shapeData.entrySet()) {
String name = e.getKey().name();
if (shapeMap.containsKey(name)) {
e.setValue(shapeMap.get(name));
} else {
Object altName = shape.language_mapping.get(name);
if (altName != null && shapeMap.containsKey(altName.toString())) {
e.setValue(shapeMap.get(altName.toString()));
}
}
}
shape.setData(shapeData);
data.put(RegionKeys.shape, shape);
} else throw new IllegalArgumentException("invalid shape\n" + shapeObj);
if (wbo) {
Shape> worldShape;
try {
worldShape = RTP.serverAccessor.getWorldBorder(world.name()).getShape().get();
} catch (IllegalStateException ignored) {
return shape;
}
if (!worldShape.equals(shape)) {
shape = worldShape;
data.put(RegionKeys.shape, shape);
}
}
return shape;
}
public VerticalAdjustor> getVert() {
Object vertObj = data.get(RegionKeys.vert);
VerticalAdjustor> vert;
if (vertObj instanceof VerticalAdjustor) {
vert = (VerticalAdjustor>) vertObj;
} else if (vertObj instanceof MemorySection) {
final Map vertMap = ((MemorySection) vertObj).getMapValues(true);
String vertName = String.valueOf(vertMap.get("name"));
Factory> factory = (Factory>) RTP.factoryMap.get(RTP.factoryNames.vert);
vert = (VerticalAdjustor>) factory.get(vertName);
EnumMap, Object> vertData = vert.getData();
for (Map.Entry extends Enum>, Object> e : vertData.entrySet()) {
String name = e.getKey().name();
if (vertMap.containsKey(name)) {
e.setValue(vertMap.get(name));
} else {
Object altName = vert.language_mapping.get(name);
if (altName != null && vertMap.containsKey(altName.toString())) {
e.setValue(vertMap.get(altName.toString()));
}
}
}
Map validY = new HashMap<>();
validY.put("minY",Math.max(getWorld().getMinHeight(),vert.minY()));
validY.put("maxY",Math.min(getWorld().getMaxHeight(),vert.maxY()));
vert.setData(vertData);
vert.setData(validY);
data.put(RegionKeys.vert, vert);
} else throw new IllegalArgumentException("invalid shape\n" + vertObj);
return vert;
}
public RTPWorld getWorld() {
Object world = data.get(RegionKeys.world);
if (world instanceof RTPWorld) return (RTPWorld) world;
else {
String worldName = String.valueOf(world);
RTPWorld rtpWorld;
if (worldName.startsWith("[") && worldName.endsWith("]")) {
int num = Integer.parseInt(worldName.substring(1, worldName.length() - 1));
rtpWorld = RTP.serverAccessor.getRTPWorlds().get(num);
} else rtpWorld = RTP.serverAccessor.getRTPWorld(worldName);
if (rtpWorld == null) rtpWorld = RTP.serverAccessor.getRTPWorlds().get(0);
data.put(RegionKeys.world, rtpWorld);
return rtpWorld;
}
}
//localized generic task for
protected class Cache extends RTPRunnable {
private static final long lastUpdate = 0;
private final UUID playerId;
public Cache() {
playerId = null;
}
public Cache(UUID playerId) {
this.playerId = playerId;
}
@Override
public void run() {
long cacheCap = getNumber(RegionKeys.cacheCap, 10L).longValue();
cacheCap = Math.max(cacheCap, playerQueue.size());
Map.Entry pair = getLocation(null);
if (pair != null) {
RTPLocation location = pair.getKey();
if (location == null) {
if (cachePipeline.size() + locationQueue.size() < cacheCap + playerQueue.size())
cachePipeline.add(new Cache());
return;
}
ConfigParser perf = (ConfigParser) RTP.configs.getParser(PerformanceKeys.class);
long radius = perf.getNumber(PerformanceKeys.viewDistanceSelect, 0L).longValue();
ChunkSet chunkSet = chunks(location, radius);
chunkSet.whenComplete(aBoolean -> {
if (aBoolean) {
if (playerId == null) {
locationQueue.add(pair);
locAssChunks.put(pair.getKey(), chunkSet);
} else if (fastLocations.containsKey(playerId) && !fastLocations.get(playerId).isDone()) {
fastLocations.get(playerId).complete(pair);
} else {
perPlayerLocationQueue.putIfAbsent(playerId, new ConcurrentLinkedQueue<>());
perPlayerLocationQueue.get(playerId).add(pair);
}
} else chunkSet.keep(false);
});
}
if (cachePipeline.size() + locationQueue.size() < cacheCap + playerQueue.size())
cachePipeline.add(new Cache());
}
}
@Override
public boolean equals(Object other) {
if(!(other instanceof Region)) return false;
Region region = (Region) other;
if(!getShape().equals(region.getShape())) return false;
if(!getVert().equals(region.getVert())) return false;
if(!getWorld().equals(region.getWorld())) return false;
return Boolean.parseBoolean(region.data.get(RegionKeys.worldBorderOverride).toString()) == Boolean.parseBoolean(data.get(RegionKeys.worldBorderOverride).toString());
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy