mirror of
https://github.com/Dreeam-qwq/Gale.git
synced 2025-12-30 03:59:08 +00:00
Gale 1.21.4
This commit is contained in:
@@ -0,0 +1,19 @@
|
||||
// Gale - JettPack - reduce array allocations
|
||||
|
||||
package me.titaniumtown;
|
||||
|
||||
public final class ArrayConstants {
|
||||
|
||||
private ArrayConstants() {}
|
||||
|
||||
public static final Object[] emptyObjectArray = new Object[0];
|
||||
public static final short[] emptyShortArray = new short[0];
|
||||
public static final int[] emptyIntArray = new int[0];
|
||||
public static final int[] zeroSingletonIntArray = new int[]{0};
|
||||
public static final byte[] emptyByteArray = new byte[0];
|
||||
public static final String[] emptyStringArray = new String[0];
|
||||
public static final long[] emptyLongArray = new long[0];
|
||||
public static final org.bukkit.entity.Entity[] emptyBukkitEntityArray = new org.bukkit.entity.Entity[0];
|
||||
public static final net.minecraft.world.entity.Entity[] emptyEntityArray = new net.minecraft.world.entity.Entity[0];
|
||||
//public static final net.minecraft.server.level.ServerLevel[] emptyServerLevelArray = new net.minecraft.server.level.ServerLevel[0];
|
||||
}
|
||||
@@ -0,0 +1,215 @@
|
||||
// Gale - Lithium - faster chunk serialization
|
||||
|
||||
package net.caffeinemc.mods.lithium.common.world.chunk;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import it.unimi.dsi.fastutil.HashCommon;
|
||||
import it.unimi.dsi.fastutil.objects.Reference2IntMap;
|
||||
import it.unimi.dsi.fastutil.objects.Reference2IntOpenHashMap;
|
||||
import net.minecraft.CrashReport;
|
||||
import net.minecraft.CrashReportCategory;
|
||||
import net.minecraft.ReportedException;
|
||||
import net.minecraft.core.IdMap;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraft.network.VarInt;
|
||||
import net.minecraft.world.level.chunk.MissingPaletteEntryException;
|
||||
import net.minecraft.world.level.chunk.Palette;
|
||||
import net.minecraft.world.level.chunk.PaletteResize;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import static it.unimi.dsi.fastutil.Hash.FAST_LOAD_FACTOR;
|
||||
|
||||
/**
|
||||
* Generally provides better performance over the vanilla {@link net.minecraft.world.level.chunk.HashMapPalette} when calling
|
||||
* {@link LithiumHashPalette#idFor(Object)} through using a faster backing map and reducing pointer chasing.
|
||||
*/
|
||||
public class LithiumHashPalette<T> implements Palette<T> {
|
||||
private static final int ABSENT_VALUE = -1;
|
||||
|
||||
private final IdMap<T> idList;
|
||||
private final PaletteResize<T> resizeHandler;
|
||||
private final int indexBits;
|
||||
|
||||
private final Reference2IntMap<T> table;
|
||||
private T[] entries;
|
||||
private int size = 0;
|
||||
|
||||
public LithiumHashPalette(IdMap<T> idList, PaletteResize<T> resizeHandler, int indexBits, T[] entries, Reference2IntMap<T> table, int size) {
|
||||
this.idList = idList;
|
||||
this.resizeHandler = resizeHandler;
|
||||
this.indexBits = indexBits;
|
||||
this.entries = entries;
|
||||
this.table = table;
|
||||
this.size = size;
|
||||
}
|
||||
|
||||
public LithiumHashPalette(IdMap<T> idList, int bits, PaletteResize<T> resizeHandler, List<T> list) {
|
||||
this(idList, bits, resizeHandler);
|
||||
|
||||
for (T t : list) {
|
||||
this.addEntry(t);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public LithiumHashPalette(IdMap<T> idList, int bits, PaletteResize<T> resizeHandler) {
|
||||
this.idList = idList;
|
||||
this.indexBits = bits;
|
||||
this.resizeHandler = resizeHandler;
|
||||
|
||||
int capacity = 1 << bits;
|
||||
|
||||
this.entries = (T[]) new Object[capacity];
|
||||
this.table = new Reference2IntOpenHashMap<>(capacity, FAST_LOAD_FACTOR);
|
||||
this.table.defaultReturnValue(ABSENT_VALUE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int idFor(T obj) {
|
||||
int id = this.table.getInt(obj);
|
||||
|
||||
if (id == ABSENT_VALUE) {
|
||||
id = this.computeEntry(obj);
|
||||
}
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean maybeHas(Predicate<T> predicate) {
|
||||
for (int i = 0; i < this.size; ++i) {
|
||||
if (predicate.test(this.entries[i])) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private int computeEntry(T obj) {
|
||||
int id = this.addEntry(obj);
|
||||
|
||||
if (id >= 1 << this.indexBits) {
|
||||
if (this.resizeHandler == null) {
|
||||
throw new IllegalStateException("Cannot grow");
|
||||
} else {
|
||||
id = this.resizeHandler.onResize(this.indexBits + 1, obj);
|
||||
}
|
||||
}
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
private int addEntry(T obj) {
|
||||
int nextId = this.size;
|
||||
|
||||
if (nextId >= this.entries.length) {
|
||||
this.resize(this.size);
|
||||
}
|
||||
|
||||
this.table.put(obj, nextId);
|
||||
this.entries[nextId] = obj;
|
||||
|
||||
this.size++;
|
||||
|
||||
return nextId;
|
||||
}
|
||||
|
||||
private void resize(int neededCapacity) {
|
||||
this.entries = Arrays.copyOf(this.entries, HashCommon.nextPowerOfTwo(neededCapacity + 1));
|
||||
}
|
||||
|
||||
@Override
|
||||
public T valueFor(int id) {
|
||||
T[] entries = this.entries;
|
||||
|
||||
T entry = null;
|
||||
if (id >= 0 && id < entries.length) {
|
||||
entry = entries[id];
|
||||
}
|
||||
|
||||
if (entry != null) {
|
||||
return entry;
|
||||
} else {
|
||||
throw this.missingPaletteEntryCrash(id);
|
||||
}
|
||||
}
|
||||
|
||||
private ReportedException missingPaletteEntryCrash(int id) {
|
||||
try {
|
||||
throw new MissingPaletteEntryException(id);
|
||||
} catch (MissingPaletteEntryException e) {
|
||||
CrashReport crashReport = CrashReport.forThrowable(e, "[Lithium] Getting Palette Entry");
|
||||
CrashReportCategory crashReportCategory = crashReport.addCategory("Chunk section");
|
||||
crashReportCategory.setDetail("IndexBits", this.indexBits);
|
||||
crashReportCategory.setDetail("Entries", this.entries.length + " Elements: " + Arrays.toString(this.entries));
|
||||
crashReportCategory.setDetail("Table", this.table.size() + " Elements: " + this.table);
|
||||
return new ReportedException(crashReport);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(FriendlyByteBuf buf) {
|
||||
this.clear();
|
||||
|
||||
int entryCount = buf.readVarInt();
|
||||
|
||||
for (int i = 0; i < entryCount; ++i) {
|
||||
this.addEntry(this.idList.byId(buf.readVarInt()));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(FriendlyByteBuf buf) {
|
||||
int size = this.size;
|
||||
buf.writeVarInt(size);
|
||||
|
||||
for (int i = 0; i < size; ++i) {
|
||||
buf.writeVarInt(this.idList.getId(this.valueFor(i)));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSerializedSize() {
|
||||
int size = VarInt.getByteSize(this.size);
|
||||
|
||||
for (int i = 0; i < this.size; ++i) {
|
||||
size += VarInt.getByteSize(this.idList.getId(this.valueFor(i)));
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSize() {
|
||||
return this.size;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Palette<T> copy(PaletteResize<T> resizeHandler) {
|
||||
return new LithiumHashPalette<>(this.idList, resizeHandler, this.indexBits, this.entries.clone(), new Reference2IntOpenHashMap<>(this.table), this.size);
|
||||
}
|
||||
|
||||
private void clear() {
|
||||
Arrays.fill(this.entries, null);
|
||||
this.table.clear();
|
||||
this.size = 0;
|
||||
}
|
||||
|
||||
public List<T> getElements() {
|
||||
ImmutableList.Builder<T> builder = new ImmutableList.Builder<>();
|
||||
for (T entry : this.entries) {
|
||||
if (entry != null) {
|
||||
builder.add(entry);
|
||||
}
|
||||
}
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
public static <A> Palette<A> create(int bits, IdMap<A> idList, PaletteResize<A> listener, List<A> list) {
|
||||
return new LithiumHashPalette<>(idList, bits, listener, list);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,179 @@
|
||||
// Gale - Gale commands - /gale command
|
||||
|
||||
package org.galemc.gale.command;
|
||||
|
||||
import io.papermc.paper.command.CommandUtil;
|
||||
import it.unimi.dsi.fastutil.Pair;
|
||||
import net.minecraft.Util;
|
||||
import org.galemc.gale.command.subcommands.InfoCommand;
|
||||
import org.galemc.gale.command.subcommands.ReloadCommand;
|
||||
import org.galemc.gale.command.subcommands.VersionCommand;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.permissions.Permission;
|
||||
import org.bukkit.permissions.PermissionDefault;
|
||||
import org.bukkit.plugin.PluginManager;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static net.kyori.adventure.text.Component.newline;
|
||||
import static net.kyori.adventure.text.Component.text;
|
||||
import static net.kyori.adventure.text.format.NamedTextColor.GRAY;
|
||||
import static net.kyori.adventure.text.format.NamedTextColor.RED;
|
||||
|
||||
public final class GaleCommand extends Command {
|
||||
public static final String COMMAND_LABEL = "gale";
|
||||
public static final String BASE_PERM = GaleCommands.COMMAND_BASE_PERM + "." + COMMAND_LABEL;
|
||||
private static final Permission basePermission = new Permission(BASE_PERM, PermissionDefault.TRUE);
|
||||
// subcommand label -> subcommand
|
||||
private static final GaleSubcommand RELOAD_SUBCOMMAND = new ReloadCommand();
|
||||
private static final GaleSubcommand VERSION_SUBCOMMAND = new VersionCommand();
|
||||
private static final GaleSubcommand INFO_SUBCOMMAND = new InfoCommand();
|
||||
private static final Map<String, GaleSubcommand> SUBCOMMANDS = Util.make(() -> {
|
||||
final Map<Set<String>, GaleSubcommand> commands = new HashMap<>();
|
||||
|
||||
commands.put(Set.of(ReloadCommand.LITERAL_ARGUMENT), RELOAD_SUBCOMMAND);
|
||||
commands.put(Set.of(VersionCommand.LITERAL_ARGUMENT), VERSION_SUBCOMMAND);
|
||||
commands.put(Set.of(InfoCommand.LITERAL_ARGUMENT), INFO_SUBCOMMAND);
|
||||
|
||||
return commands.entrySet().stream()
|
||||
.flatMap(entry -> entry.getKey().stream().map(s -> Map.entry(s, entry.getValue())))
|
||||
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
|
||||
});
|
||||
// alias -> subcommand label
|
||||
private static final Map<String, String> ALIASES = Util.make(() -> {
|
||||
final Map<String, Set<String>> aliases = new HashMap<>();
|
||||
|
||||
aliases.put(VersionCommand.LITERAL_ARGUMENT, Set.of("ver"));
|
||||
aliases.put(InfoCommand.LITERAL_ARGUMENT, Set.of("about"));
|
||||
|
||||
return aliases.entrySet().stream()
|
||||
.flatMap(entry -> entry.getValue().stream().map(s -> Map.entry(s, entry.getKey())))
|
||||
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
|
||||
});
|
||||
|
||||
private String createUsageMessage(Collection<String> arguments) {
|
||||
return "/" + COMMAND_LABEL + " [" + String.join(" | ", arguments) + "]";
|
||||
}
|
||||
|
||||
public GaleCommand() {
|
||||
super(COMMAND_LABEL);
|
||||
this.description = "Gale related commands";
|
||||
this.usageMessage = this.createUsageMessage(SUBCOMMANDS.keySet());
|
||||
final List<Permission> permissions = SUBCOMMANDS.values().stream().map(GaleSubcommand::getPermission).filter(Objects::nonNull).toList();
|
||||
this.setPermission(BASE_PERM);
|
||||
final PluginManager pluginManager = Bukkit.getServer().getPluginManager();
|
||||
pluginManager.addPermission(basePermission);
|
||||
for (final Permission permission : permissions) {
|
||||
pluginManager.addPermission(permission);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> tabComplete(
|
||||
final CommandSender sender,
|
||||
final String alias,
|
||||
final String[] args,
|
||||
final @Nullable Location location
|
||||
) throws IllegalArgumentException {
|
||||
if (args.length <= 1) {
|
||||
List<String> subCommandArguments = new ArrayList<>(SUBCOMMANDS.size());
|
||||
for (Map.Entry<String, GaleSubcommand> subCommandEntry : SUBCOMMANDS.entrySet()) {
|
||||
if (subCommandEntry.getValue().testPermission(sender)) {
|
||||
subCommandArguments.add(subCommandEntry.getKey());
|
||||
}
|
||||
}
|
||||
return CommandUtil.getListMatchingLast(sender, args, subCommandArguments);
|
||||
}
|
||||
|
||||
final @Nullable Pair<String, GaleSubcommand> subCommand = resolveCommand(args[0]);
|
||||
if (subCommand != null && subCommand.second().testPermission(sender)) {
|
||||
return subCommand.second().tabComplete(sender, subCommand.first(), Arrays.copyOfRange(args, 1, args.length));
|
||||
}
|
||||
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
private boolean testHasOnePermission(CommandSender sender) {
|
||||
for (Map.Entry<String, GaleSubcommand> subCommandEntry : SUBCOMMANDS.entrySet()) {
|
||||
if (subCommandEntry.getValue().testPermission(sender)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean execute(
|
||||
final CommandSender sender,
|
||||
final String commandLabel,
|
||||
final String[] args
|
||||
) {
|
||||
|
||||
// Check if the sender has the base permission and at least one specific permission
|
||||
if (!sender.hasPermission(basePermission) || !this.testHasOnePermission(sender)) {
|
||||
sender.sendMessage(Bukkit.permissionMessage());
|
||||
return true;
|
||||
}
|
||||
|
||||
// Determine the usage message with the subcommands they can perform
|
||||
List<String> subCommandArguments = new ArrayList<>(SUBCOMMANDS.size());
|
||||
for (Map.Entry<String, GaleSubcommand> subCommandEntry : SUBCOMMANDS.entrySet()) {
|
||||
if (subCommandEntry.getValue().testPermission(sender)) {
|
||||
subCommandArguments.add(subCommandEntry.getKey());
|
||||
}
|
||||
}
|
||||
String specificUsageMessage = this.createUsageMessage(subCommandArguments);
|
||||
|
||||
// If they did not give a subcommand
|
||||
if (args.length == 0) {
|
||||
INFO_SUBCOMMAND.execute(sender, InfoCommand.LITERAL_ARGUMENT, me.titaniumtown.ArrayConstants.emptyStringArray); // Gale - JettPack - reduce array allocations
|
||||
sender.sendMessage(newline().append(text("Command usage: " + specificUsageMessage, GRAY)));
|
||||
return false;
|
||||
}
|
||||
|
||||
// If they do not have permission for the subcommand they gave, or the argument is not a valid subcommand
|
||||
final @Nullable Pair<String, GaleSubcommand> subCommand = resolveCommand(args[0]);
|
||||
if (subCommand == null || !subCommand.second().testPermission(sender)) {
|
||||
sender.sendMessage(text("Usage: " + specificUsageMessage, RED));
|
||||
return false;
|
||||
}
|
||||
|
||||
// Execute the subcommand
|
||||
final String[] choppedArgs = Arrays.copyOfRange(args, 1, args.length);
|
||||
return subCommand.second().execute(sender, subCommand.first(), choppedArgs);
|
||||
|
||||
}
|
||||
|
||||
private static @Nullable Pair<String, GaleSubcommand> resolveCommand(String label) {
|
||||
label = label.toLowerCase(Locale.ENGLISH);
|
||||
@Nullable GaleSubcommand subCommand = SUBCOMMANDS.get(label);
|
||||
if (subCommand == null) {
|
||||
final @Nullable String command = ALIASES.get(label);
|
||||
if (command != null) {
|
||||
label = command;
|
||||
subCommand = SUBCOMMANDS.get(command);
|
||||
}
|
||||
}
|
||||
|
||||
if (subCommand != null) {
|
||||
return Pair.of(label, subCommand);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
// Gale - Gale commands
|
||||
|
||||
package org.galemc.gale.command;
|
||||
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.checkerframework.framework.qual.DefaultQualifier;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.craftbukkit.util.permissions.CraftDefaultPermissions;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@DefaultQualifier(NonNull.class)
|
||||
public final class GaleCommands {
|
||||
|
||||
public static final String COMMAND_BASE_PERM = CraftDefaultPermissions.GALE_ROOT + ".command";
|
||||
|
||||
private GaleCommands() {}
|
||||
|
||||
private static final Map<String, Command> COMMANDS = new HashMap<>();
|
||||
static {
|
||||
COMMANDS.put(GaleCommand.COMMAND_LABEL, new GaleCommand());
|
||||
}
|
||||
|
||||
public static void registerCommands(final MinecraftServer server) {
|
||||
COMMANDS.forEach((s, command) -> {
|
||||
server.server.getCommandMap().register(s, "Gale", command);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
// Gale - Gale commands
|
||||
|
||||
package org.galemc.gale.command;
|
||||
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.checkerframework.framework.qual.DefaultQualifier;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.permissions.Permission;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
@DefaultQualifier(NonNull.class)
|
||||
public interface GaleSubcommand {
|
||||
|
||||
boolean execute(CommandSender sender, String subCommand, String[] args);
|
||||
|
||||
default List<String> tabComplete(final CommandSender sender, final String subCommand, final String[] args) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
boolean testPermission(CommandSender sender);
|
||||
|
||||
@Nullable Permission getPermission();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
// Gale - Gale commands
|
||||
|
||||
package org.galemc.gale.command;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.permissions.Permission;
|
||||
import org.bukkit.permissions.PermissionDefault;
|
||||
|
||||
public abstract class PermissionedGaleSubcommand implements GaleSubcommand {
|
||||
|
||||
public final Permission permission;
|
||||
|
||||
protected PermissionedGaleSubcommand(Permission permission) {
|
||||
this.permission = permission;
|
||||
}
|
||||
|
||||
protected PermissionedGaleSubcommand(String permission, PermissionDefault permissionDefault) {
|
||||
this(new Permission(permission, permissionDefault));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean testPermission(CommandSender sender) {
|
||||
return sender.hasPermission(this.permission);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Permission getPermission() {
|
||||
return this.permission;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
// Gale - Gale commands - /gale info command
|
||||
|
||||
package org.galemc.gale.command.subcommands;
|
||||
|
||||
import net.kyori.adventure.text.event.ClickEvent;
|
||||
import net.kyori.adventure.text.format.TextDecoration;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.checkerframework.framework.qual.DefaultQualifier;
|
||||
import org.galemc.gale.command.GaleSubcommand;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.permissions.Permission;
|
||||
|
||||
import static net.kyori.adventure.text.Component.text;
|
||||
|
||||
@DefaultQualifier(NonNull.class)
|
||||
public final class InfoCommand implements GaleSubcommand {
|
||||
|
||||
public final static String LITERAL_ARGUMENT = "info";
|
||||
|
||||
@Override
|
||||
public boolean execute(final CommandSender sender, final String subCommand, final String[] args) {
|
||||
sender.sendMessage(
|
||||
text("Gale is a performant Minecraft server system. Find us on: ")
|
||||
.append(text("https://github.com/GaleMC/Gale")
|
||||
.decorate(TextDecoration.UNDERLINED)
|
||||
.clickEvent(ClickEvent.openUrl("https://github.com/GaleMC/Gale")))
|
||||
);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean testPermission(CommandSender sender) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Permission getPermission() {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
// Gale - Gale commands - /gale reload command
|
||||
|
||||
package org.galemc.gale.command.subcommands;
|
||||
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.checkerframework.framework.qual.DefaultQualifier;
|
||||
import org.galemc.gale.command.GaleCommand;
|
||||
import org.galemc.gale.command.PermissionedGaleSubcommand;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.craftbukkit.CraftServer;
|
||||
import org.bukkit.permissions.PermissionDefault;
|
||||
|
||||
import static net.kyori.adventure.text.Component.text;
|
||||
import static net.kyori.adventure.text.format.NamedTextColor.GREEN;
|
||||
import static net.kyori.adventure.text.format.NamedTextColor.RED;
|
||||
|
||||
@DefaultQualifier(NonNull.class)
|
||||
public final class ReloadCommand extends PermissionedGaleSubcommand {
|
||||
|
||||
public final static String LITERAL_ARGUMENT = "reload";
|
||||
public static final String PERM = GaleCommand.BASE_PERM + "." + LITERAL_ARGUMENT;
|
||||
|
||||
public ReloadCommand() {
|
||||
super(PERM, PermissionDefault.OP);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean execute(final CommandSender sender, final String subCommand, final String[] args) {
|
||||
this.doReload(sender);
|
||||
return true;
|
||||
}
|
||||
|
||||
private void doReload(final CommandSender sender) {
|
||||
Command.broadcastCommandMessage(sender, text("Please note that this command is not supported and may cause issues.", RED));
|
||||
Command.broadcastCommandMessage(sender, text("If you encounter any issues please use the /stop command to restart your server.", RED));
|
||||
|
||||
MinecraftServer server = ((CraftServer) sender.getServer()).getServer();
|
||||
server.galeConfigurations.reloadConfigs(server);
|
||||
server.server.reloadCount++;
|
||||
|
||||
Command.broadcastCommandMessage(sender, text("Gale config reload complete.", GREEN));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
// Gale - Gale commands - /gale version command
|
||||
|
||||
package org.galemc.gale.command.subcommands;
|
||||
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import org.checkerframework.framework.qual.DefaultQualifier;
|
||||
import org.galemc.gale.command.GaleCommand;
|
||||
import org.galemc.gale.command.PermissionedGaleSubcommand;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.permissions.PermissionDefault;
|
||||
|
||||
@DefaultQualifier(NonNull.class)
|
||||
public final class VersionCommand extends PermissionedGaleSubcommand {
|
||||
|
||||
public final static String LITERAL_ARGUMENT = "version";
|
||||
public static final String PERM = GaleCommand.BASE_PERM + "." + LITERAL_ARGUMENT;
|
||||
|
||||
public VersionCommand() {
|
||||
super(PERM, PermissionDefault.TRUE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean execute(final CommandSender sender, final String subCommand, final String[] args) {
|
||||
final @Nullable Command ver = MinecraftServer.getServer().server.getCommandMap().getCommand("version");
|
||||
if (ver != null) {
|
||||
ver.execute(sender, GaleCommand.COMMAND_LABEL, me.titaniumtown.ArrayConstants.emptyStringArray); // Gale - JettPack - reduce array allocations
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean testPermission(CommandSender sender) {
|
||||
return super.testPermission(sender) && sender.hasPermission("bukkit.command.version");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,302 @@
|
||||
// Gale - Gale configuration
|
||||
|
||||
package org.galemc.gale.configuration;
|
||||
|
||||
import com.google.common.collect.Table;
|
||||
import com.mojang.logging.LogUtils;
|
||||
import io.leangen.geantyref.TypeToken;
|
||||
import io.papermc.paper.configuration.Configuration;
|
||||
import io.papermc.paper.configuration.ConfigurationPart;
|
||||
import io.papermc.paper.configuration.Configurations;
|
||||
import io.papermc.paper.configuration.NestedSetting;
|
||||
import io.papermc.paper.configuration.PaperConfigurations;
|
||||
import io.papermc.paper.configuration.legacy.RequiresSpigotInitialization;
|
||||
import io.papermc.paper.configuration.mapping.InnerClassFieldDiscoverer;
|
||||
import io.papermc.paper.configuration.serializer.ComponentSerializer;
|
||||
import io.papermc.paper.configuration.serializer.EnumValueSerializer;
|
||||
import io.papermc.paper.configuration.serializer.PacketClassSerializer;
|
||||
import io.papermc.paper.configuration.serializer.StringRepresentableSerializer;
|
||||
import io.papermc.paper.configuration.serializer.collections.FastutilMapSerializer;
|
||||
import io.papermc.paper.configuration.serializer.collections.MapSerializer;
|
||||
import io.papermc.paper.configuration.serializer.collections.TableSerializer;
|
||||
import io.papermc.paper.configuration.serializer.registry.RegistryHolderSerializer;
|
||||
import io.papermc.paper.configuration.serializer.registry.RegistryValueSerializer;
|
||||
import io.papermc.paper.configuration.transformation.Transformations;
|
||||
import io.papermc.paper.configuration.type.BooleanOrDefault;
|
||||
import io.papermc.paper.configuration.type.Duration;
|
||||
import io.papermc.paper.configuration.type.EngineMode;
|
||||
import io.papermc.paper.configuration.type.fallback.FallbackValueSerializer;
|
||||
import io.papermc.paper.configuration.type.number.DoubleOr;
|
||||
import io.papermc.paper.configuration.type.number.IntOr;
|
||||
import it.unimi.dsi.fastutil.objects.Reference2IntMap;
|
||||
import it.unimi.dsi.fastutil.objects.Reference2IntOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.objects.Reference2LongMap;
|
||||
import it.unimi.dsi.fastutil.objects.Reference2LongOpenHashMap;
|
||||
import net.minecraft.core.RegistryAccess;
|
||||
import net.minecraft.core.registries.Registries;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.world.entity.EntityType;
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraft.world.level.levelgen.feature.ConfiguredFeature;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.slf4j.Logger;
|
||||
import org.spongepowered.configurate.ConfigurateException;
|
||||
import org.spongepowered.configurate.ConfigurationNode;
|
||||
import org.spongepowered.configurate.ConfigurationOptions;
|
||||
import org.spongepowered.configurate.NodePath;
|
||||
import org.spongepowered.configurate.objectmapping.ObjectMapper;
|
||||
import org.spongepowered.configurate.transformation.ConfigurationTransformation;
|
||||
import org.spongepowered.configurate.transformation.TransformAction;
|
||||
import org.spongepowered.configurate.yaml.YamlConfigurationLoader;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Type;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.function.Function;
|
||||
|
||||
import static io.leangen.geantyref.GenericTypeReflector.erase;
|
||||
|
||||
@SuppressWarnings("Convert2Diamond")
|
||||
public class GaleConfigurations extends Configurations<GaleGlobalConfiguration, GaleWorldConfiguration> {
|
||||
|
||||
private static final Logger LOGGER = LogUtils.getLogger();
|
||||
static final String GLOBAL_CONFIG_FILE_NAME = "gale-global.yml";
|
||||
static final String WORLD_DEFAULTS_CONFIG_FILE_NAME = "gale-world-defaults.yml";
|
||||
static final String WORLD_CONFIG_FILE_NAME = "gale-world.yml";
|
||||
public static final String CONFIG_DIR = "config";
|
||||
|
||||
private static final String GLOBAL_HEADER = String.format("""
|
||||
This is the global configuration file for Gale.
|
||||
As you can see, there's a lot to configure. Some options may impact gameplay, so use
|
||||
with caution, and make sure you know what each option does before configuring.
|
||||
|
||||
If you need help with the configuration or have any questions related to Gale,
|
||||
join us in our Discord, or check the GitHub Wiki pages.
|
||||
|
||||
The world configuration options are inside
|
||||
their respective world folder. The files are named %s
|
||||
|
||||
Wiki: https://github.com/GaleMC/Gale/wiki
|
||||
Discord: https://discord.gg/gwezNT8c24""", WORLD_CONFIG_FILE_NAME);
|
||||
|
||||
private static final String WORLD_DEFAULTS_HEADER = """
|
||||
This is the world defaults configuration file for Gale.
|
||||
As you can see, there's a lot to configure. Some options may impact gameplay, so use
|
||||
with caution, and make sure you know what each option does before configuring.
|
||||
|
||||
If you need help with the configuration or have any questions related to Gale,
|
||||
join us in our Discord, or check the GitHub Wiki pages.
|
||||
|
||||
Configuration options here apply to all worlds, unless you specify overrides inside
|
||||
the world-specific config file inside each world folder.
|
||||
|
||||
Wiki: https://github.com/GaleMC/Gale/wiki
|
||||
Discord: https://discord.gg/gwezNT8c24""";
|
||||
|
||||
private static final Function<ContextMap, String> WORLD_HEADER = map -> String.format("""
|
||||
This is a world configuration file for Gale.
|
||||
This file may start empty but can be filled with settings to override ones in the %s/%s
|
||||
|
||||
World: %s (%s)""",
|
||||
CONFIG_DIR,
|
||||
WORLD_DEFAULTS_CONFIG_FILE_NAME,
|
||||
map.require(WORLD_NAME),
|
||||
map.require(WORLD_KEY)
|
||||
);
|
||||
|
||||
private static final String MOVED_NOTICE = """
|
||||
The global and world default configuration files have moved to %s
|
||||
and the world-specific configuration file has been moved inside
|
||||
the respective world folder.
|
||||
|
||||
See https://github.com/GaleMC/Gale/wiki for more information.
|
||||
""";
|
||||
|
||||
public GaleConfigurations(final Path globalFolder) {
|
||||
super(globalFolder, GaleGlobalConfiguration.class, GaleWorldConfiguration.class, GLOBAL_CONFIG_FILE_NAME, WORLD_DEFAULTS_CONFIG_FILE_NAME, WORLD_CONFIG_FILE_NAME);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected YamlConfigurationLoader.Builder createLoaderBuilder() {
|
||||
return super.createLoaderBuilder()
|
||||
.defaultOptions(GaleConfigurations::defaultOptions);
|
||||
}
|
||||
|
||||
private static ConfigurationOptions defaultOptions(ConfigurationOptions options) {
|
||||
return options.serializers(builder -> builder
|
||||
.register(MapSerializer.TYPE, new MapSerializer(false))
|
||||
.register(new EnumValueSerializer())
|
||||
.register(new ComponentSerializer())
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ObjectMapper.Factory.Builder createGlobalObjectMapperFactoryBuilder() {
|
||||
return defaultGlobalFactoryBuilder(super.createGlobalObjectMapperFactoryBuilder());
|
||||
}
|
||||
|
||||
private static ObjectMapper.Factory.Builder defaultGlobalFactoryBuilder(ObjectMapper.Factory.Builder builder) {
|
||||
return builder.addDiscoverer(InnerClassFieldDiscoverer.globalConfig());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected YamlConfigurationLoader.Builder createGlobalLoaderBuilder() {
|
||||
return super.createGlobalLoaderBuilder()
|
||||
.defaultOptions(GaleConfigurations::defaultGlobalOptions);
|
||||
}
|
||||
|
||||
private static ConfigurationOptions defaultGlobalOptions(ConfigurationOptions options) {
|
||||
return options
|
||||
.header(GLOBAL_HEADER)
|
||||
.serializers(builder -> builder.register(new PacketClassSerializer()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public GaleGlobalConfiguration initializeGlobalConfiguration(final RegistryAccess registryAccess) throws ConfigurateException {
|
||||
GaleGlobalConfiguration configuration = super.initializeGlobalConfiguration(registryAccess);
|
||||
GaleGlobalConfiguration.set(configuration);
|
||||
return configuration;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ContextMap.Builder createDefaultContextMap(final RegistryAccess registryAccess) {
|
||||
return super.createDefaultContextMap(registryAccess)
|
||||
.put(PaperConfigurations.SPIGOT_WORLD_CONFIG_CONTEXT_KEY, PaperConfigurations.SPIGOT_WORLD_DEFAULTS);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ObjectMapper.Factory.Builder createWorldObjectMapperFactoryBuilder(final ContextMap contextMap) {
|
||||
return super.createWorldObjectMapperFactoryBuilder(contextMap)
|
||||
.addNodeResolver(new RequiresSpigotInitialization.Factory(contextMap.require(PaperConfigurations.SPIGOT_WORLD_CONFIG_CONTEXT_KEY).get()))
|
||||
.addNodeResolver(new NestedSetting.Factory())
|
||||
.addDiscoverer(InnerClassFieldDiscoverer.galeWorldConfig(contextMap));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected YamlConfigurationLoader.Builder createWorldConfigLoaderBuilder(final ContextMap contextMap) {
|
||||
final RegistryAccess access = contextMap.require(REGISTRY_ACCESS);
|
||||
return super.createWorldConfigLoaderBuilder(contextMap)
|
||||
.defaultOptions(options -> options
|
||||
.header(contextMap.require(WORLD_NAME).equals(WORLD_DEFAULTS) ? WORLD_DEFAULTS_HEADER : WORLD_HEADER.apply(contextMap))
|
||||
.serializers(serializers -> serializers
|
||||
.register(new TypeToken<Reference2IntMap<?>>() {}, new FastutilMapSerializer.SomethingToPrimitive<Reference2IntMap<?>>(Reference2IntOpenHashMap::new, Integer.TYPE))
|
||||
.register(new TypeToken<Reference2LongMap<?>>() {}, new FastutilMapSerializer.SomethingToPrimitive<Reference2LongMap<?>>(Reference2LongOpenHashMap::new, Long.TYPE))
|
||||
.register(new TypeToken<Table<?, ?, ?>>() {}, new TableSerializer())
|
||||
.register(new StringRepresentableSerializer())
|
||||
.register(IntOr.Default.SERIALIZER)
|
||||
.register(IntOr.Disabled.SERIALIZER)
|
||||
.register(DoubleOr.Default.SERIALIZER)
|
||||
.register(BooleanOrDefault.SERIALIZER)
|
||||
.register(Duration.SERIALIZER)
|
||||
.register(EngineMode.SERIALIZER)
|
||||
.register(FallbackValueSerializer.create(contextMap.require(PaperConfigurations.SPIGOT_WORLD_CONFIG_CONTEXT_KEY).get(), MinecraftServer::getServer))
|
||||
.register(new RegistryValueSerializer<>(new TypeToken<EntityType<?>>() {}, access, Registries.ENTITY_TYPE, true))
|
||||
.register(new RegistryValueSerializer<>(Item.class, access, Registries.ITEM, true))
|
||||
.register(new RegistryHolderSerializer<>(new TypeToken<ConfiguredFeature<?, ?>>() {}, access, Registries.CONFIGURED_FEATURE, false))
|
||||
.register(new RegistryHolderSerializer<>(Item.class, access, Registries.ITEM, true))
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void applyWorldConfigTransformations(final ContextMap contextMap, final ConfigurationNode node, final @Nullable ConfigurationNode defaultsNode) throws ConfigurateException {
|
||||
final ConfigurationNode version = node.node(Configuration.VERSION_FIELD);
|
||||
final String world = contextMap.require(WORLD_NAME);
|
||||
if (version.virtual()) {
|
||||
LOGGER.warn("The Gale world config file for " + world + " didn't have a version set, assuming latest");
|
||||
version.raw(GaleWorldConfiguration.CURRENT_VERSION);
|
||||
}
|
||||
if (GaleRemovedConfigurations.REMOVED_WORLD_PATHS.length > 0) {
|
||||
ConfigurationTransformation.Builder builder = ConfigurationTransformation.builder();
|
||||
for (NodePath path : GaleRemovedConfigurations.REMOVED_WORLD_PATHS) {
|
||||
builder.addAction(path, TransformAction.remove());
|
||||
}
|
||||
builder.build().apply(node);
|
||||
}
|
||||
// ADD FUTURE TRANSFORMS HERE
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void applyGlobalConfigTransformations(ConfigurationNode node) throws ConfigurateException {
|
||||
if (GaleRemovedConfigurations.REMOVED_GLOBAL_PATHS.length > 0) {
|
||||
ConfigurationTransformation.Builder builder = ConfigurationTransformation.builder();
|
||||
for (NodePath path : GaleRemovedConfigurations.REMOVED_GLOBAL_PATHS) {
|
||||
builder.addAction(path, TransformAction.remove());
|
||||
}
|
||||
builder.build().apply(node);
|
||||
}
|
||||
// ADD FUTURE TRANSFORMS HERE
|
||||
}
|
||||
|
||||
private static final List<Transformations.DefaultsAware> DEFAULT_AWARE_TRANSFORMATIONS = Collections.emptyList();
|
||||
|
||||
@Override
|
||||
protected void applyDefaultsAwareWorldConfigTransformations(final ContextMap contextMap, final ConfigurationNode worldNode, final ConfigurationNode defaultsNode) throws ConfigurateException {
|
||||
final ConfigurationTransformation.Builder builder = ConfigurationTransformation.builder();
|
||||
// ADD FUTURE TRANSFORMS HERE (these transforms run after the defaults have been merged into the node)
|
||||
DEFAULT_AWARE_TRANSFORMATIONS.forEach(transform -> transform.apply(builder, contextMap, defaultsNode));
|
||||
|
||||
ConfigurationTransformation transformation;
|
||||
try {
|
||||
transformation = builder.build(); // build throws IAE if no actions were provided (bad zml)
|
||||
} catch (IllegalArgumentException ignored) {
|
||||
return;
|
||||
}
|
||||
transformation.apply(worldNode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GaleWorldConfiguration createWorldConfig(final ContextMap contextMap) {
|
||||
final String levelName = contextMap.require(WORLD_NAME);
|
||||
try {
|
||||
return super.createWorldConfig(contextMap);
|
||||
} catch (IOException exception) {
|
||||
throw new RuntimeException("Could not create Gale world config for " + levelName, exception);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isConfigType(final Type type) {
|
||||
return ConfigurationPart.class.isAssignableFrom(erase(type));
|
||||
}
|
||||
|
||||
public void reloadConfigs(MinecraftServer server) {
|
||||
try {
|
||||
this.initializeGlobalConfiguration(reloader(this.globalConfigClass, GaleGlobalConfiguration.get()));
|
||||
this.initializeWorldDefaultsConfiguration(server.registryAccess());
|
||||
for (ServerLevel level : server.getAllLevels()) {
|
||||
this.createWorldConfig(PaperConfigurations.createWorldContextMap(level), reloader(this.worldConfigClass, level.galeConfig()));
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
throw new RuntimeException("Could not reload Gale configuration files", ex);
|
||||
}
|
||||
}
|
||||
|
||||
public static GaleConfigurations setup(final Path configDir) throws Exception {
|
||||
try {
|
||||
PaperConfigurations.createDirectoriesSymlinkAware(configDir);
|
||||
return new GaleConfigurations(configDir);
|
||||
} catch (final IOException ex) {
|
||||
throw new RuntimeException("Could not setup GaleConfigurations", ex);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int globalConfigVersion() {
|
||||
return GaleGlobalConfiguration.CURRENT_VERSION;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int worldConfigVersion() {
|
||||
return getWorldConfigurationCurrentVersion();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getWorldConfigurationCurrentVersion() {
|
||||
return GaleWorldConfiguration.CURRENT_VERSION;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,151 @@
|
||||
// Gale - Gale configuration
|
||||
|
||||
package org.galemc.gale.configuration;
|
||||
|
||||
import io.papermc.paper.configuration.Configuration;
|
||||
import io.papermc.paper.configuration.ConfigurationPart;
|
||||
import net.minecraft.world.level.levelgen.structure.PoolElementStructurePiece;
|
||||
import org.spongepowered.configurate.objectmapping.meta.PostProcess;
|
||||
import org.spongepowered.configurate.objectmapping.meta.Setting;
|
||||
import org.bukkit.plugin.java.JavaPluginLoader;
|
||||
|
||||
import java.util.Locale;
|
||||
import java.util.Random;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
@SuppressWarnings({"CanBeFinal", "FieldCanBeLocal", "FieldMayBeFinal", "NotNullFieldNotInitialized", "InnerClassMayBeStatic"})
|
||||
public class GaleGlobalConfiguration extends ConfigurationPart {
|
||||
static final int CURRENT_VERSION = 1;
|
||||
private static GaleGlobalConfiguration instance;
|
||||
public static GaleGlobalConfiguration get() {
|
||||
return instance;
|
||||
}
|
||||
static void set(GaleGlobalConfiguration instance) {
|
||||
GaleGlobalConfiguration.instance = instance;
|
||||
}
|
||||
|
||||
@Setting(Configuration.VERSION_FIELD)
|
||||
public int version = CURRENT_VERSION;
|
||||
|
||||
public SmallOptimizations smallOptimizations;
|
||||
public class SmallOptimizations extends ConfigurationPart {
|
||||
|
||||
public ReducedIntervals reducedIntervals;
|
||||
public class ReducedIntervals extends ConfigurationPart {
|
||||
|
||||
public int increaseTimeStatistics = 20; // Gale - Hydrinity - increase time statistics in intervals
|
||||
public int updateEntityLineOfSight = 4; // Gale - Petal - reduce line of sight updates
|
||||
|
||||
@PostProcess
|
||||
public void postProcess() {
|
||||
net.minecraft.world.entity.player.Player.increaseTimeStatisticsInterval = Math.max(1, increaseTimeStatistics); // Gale - Hydrinity - increase time statistics in intervals - store as static field for fast access
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Gale start - xor-shift random
|
||||
public UseXorShiftRandom useXorShiftRandom;
|
||||
public class UseXorShiftRandom extends ConfigurationPart {
|
||||
|
||||
public boolean autoReplenishLootableRefill = true;
|
||||
public boolean elytraFireworkSpeed = true;
|
||||
public boolean entityWakeUpDuration = true;
|
||||
public boolean lightningRandomTick = true;
|
||||
|
||||
@Setting("generate-tree-with-bukkit-api")
|
||||
public boolean generateTreeWithBukkitAPI = true;
|
||||
|
||||
@PostProcess
|
||||
public void postProcess() {
|
||||
com.destroystokyo.paper.loottable.PaperLootableInventoryData.RANDOM = autoReplenishLootableRefill ? new org.galemc.gale.random.XorShiftRandom() : new Random();
|
||||
io.papermc.paper.entity.activation.ActivationRange.wakeUpDurationRandom = entityWakeUpDuration ? new org.galemc.gale.random.XorShiftRandom() : new Random();
|
||||
org.bukkit.craftbukkit.CraftWorld.rand = generateTreeWithBukkitAPI ? new org.galemc.gale.random.XorShiftRandom() : new Random();
|
||||
}
|
||||
|
||||
}
|
||||
// Gale end - xor-shift random
|
||||
|
||||
}
|
||||
|
||||
public GameplayMechanics gameplayMechanics;
|
||||
public class GameplayMechanics extends ConfigurationPart {
|
||||
|
||||
public boolean enableBookWriting = true; // Gale - Pufferfish - make book writing configurable
|
||||
|
||||
}
|
||||
|
||||
public Misc misc;
|
||||
public class Misc extends ConfigurationPart {
|
||||
|
||||
public boolean verifyChatOrder = true; // Gale - Pufferfish - make chat order verification configurable
|
||||
public int premiumAccountSlowLoginTimeout = -1; // Gale - make slow login timeout configurable
|
||||
public boolean ignoreNullLegacyStructureData = false; // Gale - MultiPaper - ignore null legacy structure data
|
||||
|
||||
public Keepalive keepalive;
|
||||
public class Keepalive extends ConfigurationPart {
|
||||
public boolean sendMultiple = true; // Gale - Purpur - send multiple keep-alive packets
|
||||
}
|
||||
|
||||
// Gale start - YAPFA - last tick time - in TPS command
|
||||
public LastTickTimeInTpsCommand lastTickTimeInTpsCommand;
|
||||
public class LastTickTimeInTpsCommand extends ConfigurationPart {
|
||||
public boolean enabled = false;
|
||||
public boolean addOversleep = false;
|
||||
}
|
||||
// Gale end - YAPFA - last tick time - in TPS command
|
||||
|
||||
}
|
||||
|
||||
public LogToConsole logToConsole;
|
||||
public class LogToConsole extends ConfigurationPart { // Gale - EMC - softly log invalid pool element errors
|
||||
|
||||
public boolean invalidStatistics = true; // Gale - EMC - do not log invalid statistics
|
||||
public boolean ignoredAdvancements = true; // Gale - Purpur - do not log ignored advancements
|
||||
public boolean setBlockInFarChunk = true; // Gale - Purpur - do not log setBlock in far chunks
|
||||
public boolean unrecognizedRecipes = false; // Gale - Purpur - do not log unrecognized recipes
|
||||
public boolean legacyMaterialInitialization = false; // Gale - Purpur - do not log legacy Material initialization
|
||||
public boolean nullIdDisconnections = true; // Gale - Pufferfish - do not log disconnections with null id
|
||||
public boolean playerLoginLocations = true; // Gale - JettPack - make logging login location configurable
|
||||
|
||||
public Chat chat;
|
||||
public class Chat extends ConfigurationPart {
|
||||
public boolean emptyMessageWarning = false; // Gale - do not log empty message warnings
|
||||
public boolean expiredMessageWarning = false; // Gale - do not log expired message warnings
|
||||
public boolean notSecureMarker = true; // Gale - do not log Not Secure marker
|
||||
}
|
||||
|
||||
// Gale start - Purpur - do not log plugin library loads
|
||||
public PluginLibraryLoader pluginLibraryLoader;
|
||||
public class PluginLibraryLoader extends ConfigurationPart {
|
||||
|
||||
public boolean downloads = true;
|
||||
public boolean startLoadLibrariesForPlugin = true;
|
||||
public boolean libraryLoaded = true;
|
||||
|
||||
@PostProcess
|
||||
public void postProcess() {
|
||||
JavaPluginLoader.logDownloads = this.downloads;
|
||||
JavaPluginLoader.logStartLoadLibrariesForPlugin = this.startLoadLibrariesForPlugin;
|
||||
JavaPluginLoader.logLibraryLoaded = this.libraryLoaded;
|
||||
}
|
||||
|
||||
}
|
||||
// Gale end - Purpur - do not log plugin library loads
|
||||
|
||||
// Gale start - EMC - softly log invalid pool element errors
|
||||
public String invalidPoolElementErrorLogLevel = "info";
|
||||
public transient Consumer<String> invalidPoolElementErrorStringConsumer;
|
||||
|
||||
@PostProcess
|
||||
public void postProcess() {
|
||||
this.invalidPoolElementErrorStringConsumer = switch (this.invalidPoolElementErrorLogLevel.toLowerCase(Locale.ROOT)) {
|
||||
case "none" -> $ -> {};
|
||||
case "info", "log" -> PoolElementStructurePiece.LOGGER::info;
|
||||
case "warn", "warning" -> PoolElementStructurePiece.LOGGER::warn;
|
||||
default -> PoolElementStructurePiece.LOGGER::error;
|
||||
};
|
||||
}
|
||||
// Gale end - EMC - softly log invalid pool element errors
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
// Gale - Gale configuration
|
||||
|
||||
package org.galemc.gale.configuration;
|
||||
|
||||
import org.spongepowered.configurate.NodePath;
|
||||
|
||||
interface GaleRemovedConfigurations {
|
||||
|
||||
NodePath[] REMOVED_WORLD_PATHS = {};
|
||||
|
||||
NodePath[] REMOVED_GLOBAL_PATHS = {};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
// Gale - Gale configuration
|
||||
|
||||
package org.galemc.gale.configuration;
|
||||
|
||||
import com.mojang.logging.LogUtils;
|
||||
import io.papermc.paper.configuration.Configuration;
|
||||
import io.papermc.paper.configuration.ConfigurationPart;
|
||||
import io.papermc.paper.configuration.PaperConfigurations;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import org.slf4j.Logger;
|
||||
import org.spigotmc.SpigotWorldConfig;
|
||||
import org.spongepowered.configurate.objectmapping.meta.Setting;
|
||||
|
||||
@SuppressWarnings({"FieldCanBeLocal", "FieldMayBeFinal", "NotNullFieldNotInitialized", "InnerClassMayBeStatic"})
|
||||
public class GaleWorldConfiguration extends ConfigurationPart {
|
||||
private static final Logger LOGGER = LogUtils.getLogger();
|
||||
public static final int CURRENT_VERSION = 1;
|
||||
|
||||
private transient final SpigotWorldConfig spigotConfig;
|
||||
private transient final ResourceLocation worldKey;
|
||||
public GaleWorldConfiguration(SpigotWorldConfig spigotConfig, ResourceLocation worldKey) {
|
||||
this.spigotConfig = spigotConfig;
|
||||
this.worldKey = worldKey;
|
||||
}
|
||||
|
||||
public boolean isDefault() {
|
||||
return this.worldKey.equals(PaperConfigurations.WORLD_DEFAULTS_KEY);
|
||||
}
|
||||
|
||||
@Setting(Configuration.VERSION_FIELD)
|
||||
public int version = CURRENT_VERSION;
|
||||
|
||||
public SmallOptimizations smallOptimizations;
|
||||
public class SmallOptimizations extends ConfigurationPart {
|
||||
|
||||
public boolean saveFireworks = true; // Gale - EMC - make saving fireworks configurable
|
||||
public boolean useOptimizedSheepOffspringColor = true; // Gale - carpet-fixes - optimize sheep offspring color
|
||||
|
||||
// Gale start - Airplane - reduce projectile chunk loading
|
||||
public MaxProjectileChunkLoads maxProjectileChunkLoads;
|
||||
public class MaxProjectileChunkLoads extends ConfigurationPart {
|
||||
|
||||
public int perTick = 10;
|
||||
|
||||
public PerProjectile perProjectile;
|
||||
public class PerProjectile extends ConfigurationPart {
|
||||
public int max = 10;
|
||||
public boolean resetMovementAfterReachLimit = false;
|
||||
public boolean removeFromWorldAfterReachLimit = false;
|
||||
}
|
||||
|
||||
}
|
||||
// Gale end - Airplane - reduce projectile chunk loading
|
||||
|
||||
public ReducedIntervals reducedIntervals;
|
||||
public class ReducedIntervals extends ConfigurationPart {
|
||||
|
||||
public int acquirePoiForStuckEntity = 60; // Gale - Airplane - reduce acquire POI for stuck entities
|
||||
public int checkStuckInWall = 10; // Gale - Pufferfish - reduce in wall checks
|
||||
public int villagerItemRepickup = 100; // Gale - EMC - reduce villager item re-pickup
|
||||
|
||||
public CheckNearbyItem checkNearbyItem;
|
||||
public class CheckNearbyItem extends ConfigurationPart {
|
||||
|
||||
// Gale start - EMC - reduce hopper item checks
|
||||
public Hopper hopper;
|
||||
public class Hopper extends ConfigurationPart {
|
||||
|
||||
public int interval = 1;
|
||||
|
||||
public Minecart minecart;
|
||||
public class Minecart extends ConfigurationPart {
|
||||
|
||||
public int interval = 1;
|
||||
|
||||
public TemporaryImmunity temporaryImmunity;
|
||||
public class TemporaryImmunity extends ConfigurationPart {
|
||||
public int duration = 100;
|
||||
public int nearbyItemMaxAge = 1200;
|
||||
public int checkForMinecartNearItemInterval = 20;
|
||||
public boolean checkForMinecartNearItemWhileInactive = true;
|
||||
public double maxItemHorizontalDistance = 24.0;
|
||||
public double maxItemVerticalDistance = 4.0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
// Gale end - EMC - reduce hopper item checks
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public LoadChunks loadChunks;
|
||||
public class LoadChunks extends ConfigurationPart {
|
||||
public boolean toSpawnPhantoms = false; // Gale - MultiPaper - don't load chunks to spawn phantoms
|
||||
public boolean toActivateClimbingEntities = false; // Gale - don't load chunks to activate climbing entities
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public GameplayMechanics gameplayMechanics;
|
||||
public class GameplayMechanics extends ConfigurationPart {
|
||||
|
||||
public Fixes fixes;
|
||||
public class Fixes extends ConfigurationPart {
|
||||
|
||||
public boolean broadcastCritAnimationsAsTheEntityBeingCritted = false; // Gale - MultiPaper - broadcast crit animations as the entity being critted
|
||||
|
||||
// Gale start - Purpur - fix MC-238526
|
||||
@Setting("mc-238526")
|
||||
public boolean mc238526 = false;
|
||||
// Gale end - Purpur - fix MC-238526
|
||||
|
||||
// Gale start - Purpur - fix MC-121706
|
||||
@Setting("mc-121706")
|
||||
public boolean mc121706 = false;
|
||||
// Gale end - Purpur - fix MC-121706
|
||||
|
||||
}
|
||||
|
||||
public boolean arrowMovementResetsDespawnCounter = true; // Gale - Purpur - make arrow movement resetting despawn counter configurable
|
||||
public boolean entitiesCanRandomStrollIntoNonTickingChunks = true; // Gale - MultiPaper - prevent entities random strolling into non-ticking chunks
|
||||
public double entityWakeUpDurationRatioStandardDeviation = 0.2; // Gale - variable entity wake-up duration
|
||||
public boolean hideFlamesOnEntitiesWithFireResistance = false; // Gale - Slice - hide flames on entities with fire resistance
|
||||
public boolean tryRespawnEnderDragonAfterEndCrystalPlace = true; // Gale - Pufferfish - make ender dragon respawn attempt after placing end crystals configurable
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,404 @@
|
||||
// Gale - xor-shift random
|
||||
|
||||
package org.galemc.gale.random;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
/**
|
||||
* A random number generator based on the simple and fast xor-shift pseudo
|
||||
* random number generator (RNG) specified in:
|
||||
* <br>
|
||||
* Marsaglia, George. (2003).
|
||||
* <a href="https://www.researchgate.net/publication/5142825_Xorshift_RNGs">Xorshift RNGs</a>.
|
||||
* <br>
|
||||
* Code from:
|
||||
* <br>
|
||||
* <a href="https://gist.github.com/Xyene/4637619">Random.java</a>
|
||||
* by <a href="https://gist.github.com/Xyene">Xyene</a>.
|
||||
* <br>
|
||||
* Translated from:
|
||||
* <br>
|
||||
* <a href="http://www.codeproject.com/Articles/9187/A-fast-equivalent-for-System-Random">
|
||||
* A fast equivalent for System.Random</a> by <a href="https://www.codeproject.com/Members/colgreen">colgreen</a>.
|
||||
* <br>
|
||||
* Licensed under <a href="https://www.gnu.org/licenses/lgpl-3.0.html">GNU Lesser General Public License version 3</a>.
|
||||
*/
|
||||
public final class XorShiftRandom extends Random {
|
||||
final double REAL_UNIT_INT = 1.0 / (0x7FFFFFFFL);
|
||||
final double REAL_UNIT_UINT = 1.0 / (0xFFFFFFFFL);
|
||||
final long Y = 842502087L, Z = 3579807591L, W = 273326509L;
|
||||
long x, y, z, w;
|
||||
|
||||
public XorShiftRandom() {
|
||||
seed((int) System.currentTimeMillis());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSeed(long seed) {
|
||||
seed((int) seed);
|
||||
}
|
||||
|
||||
public void seed(int seed) {
|
||||
// The only stipulation stated for the xorshift RNG is that at least one of
|
||||
// the seeds x,y,z,w is non-zero. We fulfill that requirement by only allowing
|
||||
// resetting of the x seed
|
||||
x = seed;
|
||||
y = Y;
|
||||
z = Z;
|
||||
w = W;
|
||||
}
|
||||
|
||||
long boolBuffer;
|
||||
int boolBufferBits = 0;
|
||||
|
||||
@Override
|
||||
public boolean nextBoolean() {
|
||||
if (boolBufferBits == 0) {
|
||||
boolBuffer = nextUInt();
|
||||
boolBufferBits = 32;
|
||||
}
|
||||
boolBuffer >>= 1;
|
||||
boolean bit = (boolBuffer & 1) == 0;
|
||||
--boolBufferBits;
|
||||
return bit;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void nextBytes(byte[] buffer) {
|
||||
// Fill up the bulk of the buffer in chunks of 4 bytes at a time.
|
||||
long x = this.x, y = this.y, z = this.z, w = this.w;
|
||||
int i = 0;
|
||||
long t;
|
||||
for (int bound = buffer.length - 3; i < bound; ) {
|
||||
// Generate 4 bytes.
|
||||
// Increased performance is achieved by generating 4 random bytes per loop.
|
||||
// Also note that no mask needs to be applied to zero out the higher order bytes before
|
||||
// casting because the cast ignores thos bytes. Thanks to Stefan Trosch黷z for pointing this out.
|
||||
t = (x ^ (x << 11));
|
||||
x = y;
|
||||
y = z;
|
||||
z = w;
|
||||
w = (w ^ (w >> 19)) ^ (t ^ (t >> 8));
|
||||
|
||||
buffer[i++] = (byte) w;
|
||||
buffer[i++] = (byte) (w >> 8);
|
||||
buffer[i++] = (byte) (w >> 16);
|
||||
buffer[i++] = (byte) (w >> 24);
|
||||
}
|
||||
|
||||
// Fill up any remaining bytes in the buffer.
|
||||
if (i < buffer.length) {
|
||||
// Generate 4 bytes.
|
||||
t = (x ^ (x << 11));
|
||||
x = y;
|
||||
y = z;
|
||||
z = w;
|
||||
w = (w ^ (w >> 19)) ^ (t ^ (t >> 8));
|
||||
|
||||
buffer[i++] = (byte) w;
|
||||
if (i < buffer.length) {
|
||||
buffer[i++] = (byte) (w >> 8);
|
||||
if (i < buffer.length) {
|
||||
buffer[i++] = (byte) (w >> 16);
|
||||
if (i < buffer.length) {
|
||||
buffer[i] = (byte) (w >> 24);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.z = z;
|
||||
this.w = w;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double nextDouble() {
|
||||
long t = (x ^ (x << 11));
|
||||
x = y;
|
||||
y = z;
|
||||
z = w;
|
||||
|
||||
// Here we can gain a 2x speed improvement by generating a value that can be cast to
|
||||
// an int instead of the more easily available uint. If we then explicitly cast to an
|
||||
// int the compiler will then cast the int to a double to perform the multiplication,
|
||||
// this final cast is a lot faster than casting from a uint to a double. The extra cast
|
||||
// to an int is very fast (the allocated bits remain the same) and so the overall effect
|
||||
// of the extra cast is a significant performance improvement.
|
||||
//
|
||||
// Also note that the loss of one bit of precision is equivalent to what occurs within
|
||||
// System.Random.
|
||||
return (REAL_UNIT_INT * (int) (0x7FFFFFFF & (w = (w ^ (w >> 19)) ^ (t ^ (t >> 8)))));
|
||||
}
|
||||
|
||||
public double random() {
|
||||
return nextDouble();
|
||||
}
|
||||
|
||||
@Override
|
||||
public float nextFloat() {
|
||||
return (float) nextDouble();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int nextInt() {
|
||||
long t = (x ^ (x << 11));
|
||||
x = y;
|
||||
y = z;
|
||||
z = w;
|
||||
return (int) (0x7FFFFFFF & (w = (w ^ (w >> 19)) ^ (t ^ (t >> 8))));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int nextInt(int upperBound) {
|
||||
if (upperBound < 0)
|
||||
throw new IllegalArgumentException("upperBound must be >=0");
|
||||
|
||||
long t = (x ^ (x << 11));
|
||||
x = y;
|
||||
y = z;
|
||||
z = w;
|
||||
|
||||
return (int) ((REAL_UNIT_INT * (int) (0x7FFFFFFF & (w = (w ^ (w >> 19)) ^ (t ^ (t >> 8))))) * upperBound);
|
||||
}
|
||||
|
||||
public int nextInt(int lowerBound, int upperBound) {
|
||||
if (lowerBound > upperBound)
|
||||
throw new IllegalArgumentException("upperBound must be >=lowerBound");
|
||||
|
||||
long t = (x ^ (x << 11));
|
||||
x = y;
|
||||
y = z;
|
||||
z = w;
|
||||
|
||||
// The explicit int cast before the first multiplication gives better performance.
|
||||
// See comments in NextDouble.
|
||||
int range = upperBound - lowerBound;
|
||||
if (range < 0) {
|
||||
// If range is <0 then an overflow has occured and must resort to using long integer arithmetic instead (slower).
|
||||
// We also must use all 32 bits of precision, instead of the normal 31, which again is slower.
|
||||
return lowerBound + (int) ((REAL_UNIT_UINT * (double) (w = (w ^ (w >> 19)) ^ (t ^ (t >> 8)))) * (double) ((long) upperBound - (long) lowerBound));
|
||||
}
|
||||
// 31 bits of precision will suffice if range<=int.MaxValue. This allows us to cast to an int and gain
|
||||
// a little more performance.
|
||||
return lowerBound + (int) ((REAL_UNIT_INT * (double) (int) (0x7FFFFFFF & (w = (w ^ (w >> 19)) ^ (t ^ (t >> 8))))) * (double) range);
|
||||
}
|
||||
|
||||
public long nextUInt() {
|
||||
long t = (x ^ (x << 11));
|
||||
x = y;
|
||||
y = z;
|
||||
z = w;
|
||||
return (w = (w ^ (w >> 19)) ^ (t ^ (t >> 8))) & (0xFFFFFFFFL);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long nextLong() {
|
||||
return nextUInt() << 32 + nextUInt();
|
||||
}
|
||||
|
||||
double gaussNext;
|
||||
boolean hasGaussNext;
|
||||
final double TWOPI = Math.PI * 2;
|
||||
|
||||
/**
|
||||
* Get a random number in the range [min, max) or [min, max] depending on rounding.
|
||||
*
|
||||
* @param min Low bound
|
||||
* @param max High bound
|
||||
* @return A uniformly distributed double
|
||||
*/
|
||||
public double uniform(double min, double max) {
|
||||
return min + (max - min) * nextDouble();
|
||||
}
|
||||
|
||||
/**
|
||||
* Triangular distribution.
|
||||
* <p/>
|
||||
* Continuous distribution bounded by given lower and upper limits,
|
||||
* and having a given mode value in-between.
|
||||
* http://en.wikipedia.org/wiki/Triangular_distribution
|
||||
*
|
||||
* @param low Low bound
|
||||
* @param high High bound
|
||||
* @param mode Mode
|
||||
* @return A number from the triangular distribution specified
|
||||
*/
|
||||
public double triangular(int low, int high, int mode) {
|
||||
double u = nextDouble();
|
||||
double c = (mode - low) / (high - low);
|
||||
if (u > c) {
|
||||
u = 1.0 - u;
|
||||
c = 1.0 - c;
|
||||
int k = low;
|
||||
low = high;
|
||||
high = k;
|
||||
}
|
||||
return low + (high - low) * Math.sqrt(u * c);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gaussian distribution, mean is 0 and standard deviation is 1.
|
||||
* <p/>
|
||||
* mu is the mean, and sigma is the standard deviation.
|
||||
*
|
||||
* @return A double in Gaussian distribution
|
||||
*/
|
||||
public double gauss() {
|
||||
return nextGaussian();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gaussian distribution, with user-specified mean and standard deviation.
|
||||
* <p/>
|
||||
* mu is the mean, and sigma is the standard deviation.
|
||||
*
|
||||
* @return A double in Gaussian distribution
|
||||
*/
|
||||
public double gauss(double mu, double sigma) {
|
||||
return mu + sigma * nextGaussian();
|
||||
}
|
||||
|
||||
public double gaussUnsigned(double mu, double sigma) {
|
||||
double out = gauss(mu, sigma);
|
||||
return out > 1 ? out : 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Log normal distribution.
|
||||
* <p/>
|
||||
* If you take the natural logarithm of this distribution, you'll get a
|
||||
* normal distribution with mean mu and standard deviation sigma.
|
||||
* mu can have any value, and sigma must be greater than zero.
|
||||
*
|
||||
* @param mu Mean
|
||||
* @param sigma Standard deviation
|
||||
* @return A number from the log normal distribution specified
|
||||
*/
|
||||
public double logNormal(double mu, double sigma) {
|
||||
return Math.exp(gauss(mu, sigma));
|
||||
}
|
||||
|
||||
/**
|
||||
* Exponential distribution.
|
||||
* <p/>
|
||||
* lambda is 1.0 divided by the desired mean. It should be
|
||||
* nonzero. Returned values range from 0 to positive infinity
|
||||
* if lambda is positive, and from negative infinity to 0
|
||||
* if lambda is negative.
|
||||
*
|
||||
* @param lambda A non-zero value
|
||||
*/
|
||||
public double exponential(double lambda) {
|
||||
return -Math.log(1.0 - random()) / lambda;
|
||||
}
|
||||
|
||||
/**
|
||||
* Circular data distribution.
|
||||
* <p/>
|
||||
* If kappa is equal to zero, this distribution reduces
|
||||
* to a uniform random angle over the range 0 to 2*pi.
|
||||
*
|
||||
* @param mu the mean angle, expressed in radians between 0 and 2*pi.
|
||||
* @param kappa the concentration parameter, which must be greater than or
|
||||
* equal to zero.
|
||||
* @return A number from the circular data distribution specified
|
||||
*/
|
||||
public double circularData(double mu, double kappa) {
|
||||
if (kappa <= 1e-6)
|
||||
return TWOPI * nextDouble();
|
||||
|
||||
double a = 1.0 + Math.sqrt(1.0 + 4.0 * kappa * kappa);
|
||||
double b = (a - Math.sqrt(2.0 * a)) / (2.0 * kappa);
|
||||
double r = (1.0 + b * b) / (2.0 * b);
|
||||
double u1, u2, u3, f, c, z, theta = 0;
|
||||
|
||||
while (true) {
|
||||
u1 = nextDouble();
|
||||
|
||||
z = Math.cos(Math.PI * u1);
|
||||
f = (1.0 + r * z) / (r + z);
|
||||
c = kappa * (r - f);
|
||||
|
||||
u2 = nextDouble();
|
||||
|
||||
if (u2 < c * (2.0 - c) || u2 <= c * Math.exp(1.0 - c))
|
||||
break;
|
||||
|
||||
u3 = nextDouble();
|
||||
if (u3 > 0.5)
|
||||
theta = (mu % TWOPI) + Math.acos(f);
|
||||
else
|
||||
theta = (mu % TWOPI) - Math.acos(f);
|
||||
}
|
||||
return theta;
|
||||
}
|
||||
|
||||
|
||||
final double LOG4 = Math.log(4);
|
||||
final double SG_MAGICCONST = 1.0 + Math.log(4.5);
|
||||
|
||||
/**
|
||||
* Gamma distribution. Not the gamma function!
|
||||
* Conditions on the parameters are alpha > 0 and beta > 0.
|
||||
* <p/>
|
||||
* The probability distribution function is:
|
||||
* pdf(x) = (x ** (alpha - 1) * math.exp(-x / beta)) / (math.gamma(alpha) * beta ** alpha)
|
||||
*
|
||||
* @param alpha Alpha
|
||||
* @param beta Beta
|
||||
* @return A number from the gamma distribution specified
|
||||
*/
|
||||
public double gamma(double alpha, double beta) {
|
||||
if (alpha <= 0.0 || beta <= 0.0)
|
||||
throw new IllegalArgumentException("alpha and beta must be > 0.0");
|
||||
|
||||
if (alpha > 1.0) {
|
||||
double ainv = Math.sqrt(2.0 * alpha - 1.0);
|
||||
double bbb = alpha - LOG4;
|
||||
double ccc = alpha + ainv;
|
||||
double u1, u2, v, x, z, r;
|
||||
|
||||
while (true) {
|
||||
u1 = random();
|
||||
if (!(1e-7 < u1 && u1 < .9999999))
|
||||
continue;
|
||||
u2 = 1.0 - random();
|
||||
v = Math.log(u1 / (1.0 - u1)) / ainv;
|
||||
x = alpha * Math.exp(v);
|
||||
z = u1 * u1 * u2;
|
||||
r = bbb + ccc * v - x;
|
||||
if (r + SG_MAGICCONST - 4.5 * z >= 0.0 || r >= Math.log(z))
|
||||
return x * beta;
|
||||
}
|
||||
} else if (alpha == 1.0) {
|
||||
// exponential(1)
|
||||
double u;
|
||||
u = random();
|
||||
while (u <= 1e-7)
|
||||
u = random();
|
||||
return -Math.log(u) * beta;
|
||||
} else {
|
||||
// alpha is between 0 and 1 (exclusive)
|
||||
// Uses ALGORITHM GS of Statistical Computing -Kennedy & Gentle
|
||||
|
||||
double u, b, p, x, u1;
|
||||
while (true) {
|
||||
u = random();
|
||||
b = (Math.E + alpha) / Math.E;
|
||||
p = b * u;
|
||||
if (p <= 1.0)
|
||||
x = Math.pow(p, (1.0 / alpha));
|
||||
else
|
||||
x = -Math.log((b - p) / alpha);
|
||||
u1 = random();
|
||||
if (p > 1.0) {
|
||||
if (u1 <= Math.pow(x, (alpha - 1.0)))
|
||||
break;
|
||||
} else if (u1 <= Math.exp(-x))
|
||||
break;
|
||||
}
|
||||
return x * beta;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,154 @@
|
||||
// Gale - branding changes - version fetcher
|
||||
|
||||
package org.galemc.gale.version;
|
||||
|
||||
import com.destroystokyo.paper.PaperVersionFetcher;
|
||||
import com.destroystokyo.paper.VersionHistoryManager;
|
||||
import com.destroystokyo.paper.util.VersionFetcher;
|
||||
import com.google.common.base.Charsets;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonSyntaxException;
|
||||
import com.mojang.logging.LogUtils;
|
||||
import io.papermc.paper.ServerBuildInfo;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.event.ClickEvent;
|
||||
import net.kyori.adventure.text.format.NamedTextColor;
|
||||
import net.kyori.adventure.text.format.TextDecoration;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URI;
|
||||
import java.util.Optional;
|
||||
|
||||
import static net.kyori.adventure.text.Component.text;
|
||||
import static net.kyori.adventure.text.format.TextColor.color;
|
||||
|
||||
/**
|
||||
* An abstract version fetcher, derived from {@link PaperVersionFetcher}.
|
||||
* This class was then made to be a superclass of both {@link PaperVersionFetcher}
|
||||
* and {@link GaleVersionFetcher}.
|
||||
* <br>
|
||||
* Changes to {@link PaperVersionFetcher} are indicated by Gale marker comments.
|
||||
*/
|
||||
public abstract class AbstractPaperVersionFetcher implements VersionFetcher {
|
||||
protected static final Logger LOGGER = LogUtils.getClassLogger();
|
||||
protected static final int DISTANCE_ERROR = -1;
|
||||
protected static final int DISTANCE_UNKNOWN = -2;
|
||||
protected static final ServerBuildInfo BUILD_INFO = ServerBuildInfo.buildInfo();
|
||||
|
||||
// Gale start - branding changes - version fetcher
|
||||
protected final String gitHubBranchName;
|
||||
protected final String downloadPage;
|
||||
protected final String organizationDisplayName;
|
||||
protected final String projectDisplayName;
|
||||
protected final String gitHubOrganizationName;
|
||||
protected final String gitHubRepoName;
|
||||
|
||||
protected AbstractPaperVersionFetcher(String githubBranchName, String downloadPage, String organizationDisplayName, String projectDisplayName, String gitHubOrganizationName, String gitHubRepoName) {
|
||||
this.gitHubBranchName = githubBranchName;
|
||||
this.downloadPage = downloadPage;
|
||||
this.organizationDisplayName = organizationDisplayName;
|
||||
this.projectDisplayName = projectDisplayName;
|
||||
this.gitHubOrganizationName = gitHubOrganizationName;
|
||||
this.gitHubRepoName = gitHubRepoName;
|
||||
}
|
||||
// Gale end - branding changes - version fetcher
|
||||
|
||||
@Override
|
||||
public long getCacheTime() {
|
||||
return 720000;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Component getVersionMessage(final @NotNull String serverVersion) {
|
||||
final Component updateMessage;
|
||||
final ServerBuildInfo build = ServerBuildInfo.buildInfo();
|
||||
if (build.buildNumber().isEmpty() && build.gitCommit().isEmpty()) {
|
||||
updateMessage = text("You are running a development version without access to version information", color(0xFF5300));
|
||||
} else {
|
||||
updateMessage = getUpdateStatusMessage(this.gitHubOrganizationName + "/" + this.gitHubRepoName, build); // Gale - branding changes - version fetcher
|
||||
}
|
||||
final @Nullable Component history = this.getHistory();
|
||||
|
||||
return history != null ? Component.textOfChildren(updateMessage, Component.newline(), history) : updateMessage;
|
||||
}
|
||||
|
||||
// Gale start - branding changes - version fetcher
|
||||
protected boolean canFetchDistanceFromSiteApi() {
|
||||
return false;
|
||||
}
|
||||
|
||||
protected int fetchDistanceFromSiteApi(int jenkinsBuild) {
|
||||
return -1;
|
||||
}
|
||||
// Gale end - branding changes - version fetcher
|
||||
|
||||
private Component getUpdateStatusMessage(final String repo, final ServerBuildInfo build) {
|
||||
int distance = DISTANCE_ERROR;
|
||||
|
||||
// Gale start - branding changes - version fetcher
|
||||
final Optional<String> gitBranch = build.gitBranch();
|
||||
final Optional<String> gitCommit = build.gitCommit();
|
||||
if (gitBranch.isPresent() && gitCommit.isPresent()) {
|
||||
distance = fetchDistanceFromGitHub(repo, gitBranch.get(), gitCommit.get());
|
||||
}
|
||||
// Gale end - branding changes - version fetcher
|
||||
|
||||
return switch (distance) {
|
||||
case DISTANCE_ERROR -> text("Error obtaining version information", NamedTextColor.YELLOW);
|
||||
case 0 -> text("You are running the latest version", NamedTextColor.GREEN);
|
||||
case DISTANCE_UNKNOWN -> text("Unknown version", NamedTextColor.YELLOW);
|
||||
default -> text("You are " + distance + " version(s) behind", NamedTextColor.YELLOW)
|
||||
.append(Component.newline())
|
||||
.append(text("Download the new version at: ")
|
||||
.append(text(this.downloadPage, NamedTextColor.GOLD) // Gale - branding changes - version fetcher
|
||||
.hoverEvent(text("Click to open", NamedTextColor.WHITE))
|
||||
.clickEvent(ClickEvent.openUrl(this.downloadPage)))); // Gale - branding changes - version fetcher
|
||||
};
|
||||
}
|
||||
|
||||
// Contributed by Techcable <Techcable@outlook.com> in GH-65
|
||||
private static int fetchDistanceFromGitHub(final String repo, final String branch, final String hash) {
|
||||
try {
|
||||
final HttpURLConnection connection = (HttpURLConnection) URI.create("https://api.github.com/repos/%s/compare/%s...%s".formatted(repo, branch, hash)).toURL().openConnection();
|
||||
connection.connect();
|
||||
if (connection.getResponseCode() == HttpURLConnection.HTTP_NOT_FOUND)
|
||||
return DISTANCE_UNKNOWN; // Unknown commit
|
||||
try (final BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream(), Charsets.UTF_8))) {
|
||||
final JsonObject obj = new Gson().fromJson(reader, JsonObject.class);
|
||||
final String status = obj.get("status").getAsString();
|
||||
return switch (status) {
|
||||
case "identical" -> 0;
|
||||
case "behind" -> obj.get("behind_by").getAsInt();
|
||||
default -> DISTANCE_ERROR;
|
||||
};
|
||||
} catch (final JsonSyntaxException | NumberFormatException e) {
|
||||
LOGGER.error("Error parsing json from GitHub's API", e);
|
||||
return DISTANCE_ERROR;
|
||||
}
|
||||
} catch (final IOException e) {
|
||||
LOGGER.error("Error while parsing version", e);
|
||||
return DISTANCE_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
private @Nullable Component getHistory() {
|
||||
final VersionHistoryManager.VersionData data = VersionHistoryManager.INSTANCE.getVersionData();
|
||||
if (data == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final @Nullable String oldVersion = data.getOldVersion();
|
||||
if (oldVersion == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return text("Previous version: " + oldVersion, NamedTextColor.GRAY, TextDecoration.ITALIC);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
// Gale - semantic version
|
||||
|
||||
package org.galemc.gale.version;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* A holder for the Gale semantic version.
|
||||
*/
|
||||
public final class GaleSemanticVersion {
|
||||
|
||||
private GaleSemanticVersion() {
|
||||
throw new RuntimeException();
|
||||
}
|
||||
|
||||
/**
|
||||
* A semantic version in the format "<code>major.minor.patch</code>", for example "<code>1.5.1</code>".
|
||||
* The <code>major</code> version is incremented when a large and overarching set of features, with a large
|
||||
* and overarching common goal or effect, has been added compared to the first release with that major version.
|
||||
* The <code>minor</code> version is incremented for each build that has a different intended feature set
|
||||
* (for example, some features or part of them were added or removed).
|
||||
* The <code>patch</code> version is incremented for small changes that do not affect the goal of any feature,
|
||||
* such as bug fixes, performance improvements or changes in wording.
|
||||
*/
|
||||
public static final @NotNull String version = "0.6.15";
|
||||
|
||||
/**
|
||||
* The "<code>major.minor</code>" portion of the {@link #version}.
|
||||
*/
|
||||
public static final @NotNull String majorMinorVersion;
|
||||
static {
|
||||
int firstDotIndex = version.indexOf('.');
|
||||
int secondDotIndex = version.indexOf('.', firstDotIndex + 1);
|
||||
majorMinorVersion = version.substring(0, secondDotIndex);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
// Gale - branding changes - version fetcher
|
||||
|
||||
package org.galemc.gale.version;
|
||||
|
||||
public class GaleVersionFetcher extends AbstractPaperVersionFetcher {
|
||||
|
||||
public GaleVersionFetcher() {
|
||||
super(
|
||||
"ver/1.21.3",
|
||||
"https://github.com/Dreeam-qwq/Gale",
|
||||
"GaleMC",
|
||||
"Gale",
|
||||
"GaleMC",
|
||||
"Gale");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
// Gale - virtual thread support
|
||||
|
||||
package org.galemc.gale.virtualthread;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.ThreadFactory;
|
||||
|
||||
/**
|
||||
* An implementation of {@link VirtualThreadService} that can create virtual threads directly.
|
||||
*
|
||||
* @author Martijn Muijsers
|
||||
*/
|
||||
final class DirectVirtualThreadService extends VirtualThreadService {
|
||||
|
||||
private DirectVirtualThreadService() {
|
||||
super();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull ThreadFactory createFactory() {
|
||||
// Disabled until Minecraft requires servers to have a Java version that can read class files compiled with functionality from Java 19+ on preview / Java 21+ on stable
|
||||
//throw new UnsupportedOperationException();
|
||||
return Thread.ofVirtual().factory();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Thread start(@NotNull Runnable task) {
|
||||
// Disabled until Minecraft requires servers to have a Java version that can read class files compiled with functionality from Java 19+ on preview / Java 21+ on stable
|
||||
//throw new UnsupportedOperationException();
|
||||
Objects.requireNonNull(task, "The task to start a virtual thread cannot be null");
|
||||
return Thread.ofVirtual().start(task);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return A functional {@link DirectVirtualThreadService}.
|
||||
* @throws Throwable If creating virtual threads directly is not supported by the current runtime.
|
||||
* This could be any {@link Throwable}, including an {@link Exception} or an {@link Error}.
|
||||
*/
|
||||
static @NotNull DirectVirtualThreadService create() throws Throwable {
|
||||
// Disabled until Minecraft requires servers to have a Java version that can read class files compiled with functionality from Java 19+ on preview / Java 21+ on stable
|
||||
//throw new UnsupportedOperationException();
|
||||
var service = new DirectVirtualThreadService();
|
||||
// Run some tests to verify
|
||||
service.runTest();
|
||||
// If we end up here, it works
|
||||
return service;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
// Gale - virtual thread support
|
||||
|
||||
package org.galemc.gale.virtualthread;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.ThreadFactory;
|
||||
|
||||
/**
|
||||
* An implementation of {@link VirtualThreadService} that can create virtual threads using Java reflection.
|
||||
*
|
||||
* @author Martijn Muijsers
|
||||
*/
|
||||
final class ReflectionVirtualThreadService extends VirtualThreadService {
|
||||
|
||||
/**
|
||||
* The {@link Thread}<code>#ofVirtual()</code> method.
|
||||
*/
|
||||
private final @NotNull Method Thread_ofVirtual_method;
|
||||
|
||||
/**
|
||||
* The {@link Thread}<code>.Builder#factory()</code> method.
|
||||
*/
|
||||
private final @NotNull Method Thread_Builder_factory_method;
|
||||
|
||||
/**
|
||||
* The {@link Thread}<code>.Builder#start(Runnable)</code> method.
|
||||
*/
|
||||
private final @NotNull Method Thread_Builder_start_method;
|
||||
|
||||
private ReflectionVirtualThreadService() throws Throwable {
|
||||
this.Thread_ofVirtual_method = Objects.requireNonNull(Thread.class.getMethod("ofVirtual"));
|
||||
// The Thread.Builder class
|
||||
var Thread_Builder_class = Objects.requireNonNull(Class.forName("java.lang.Thread$Builder"));
|
||||
this.Thread_Builder_factory_method = Objects.requireNonNull(Thread_Builder_class.getMethod("factory"));
|
||||
this.Thread_Builder_start_method = Objects.requireNonNull(Thread_Builder_class.getMethod("start", Runnable.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull ThreadFactory createFactory() {
|
||||
try {
|
||||
return (ThreadFactory) this.Thread_Builder_factory_method.invoke(this.Thread_ofVirtual_method.invoke(null));
|
||||
} catch (Exception e) {
|
||||
// This should not be possible because it was tested in create()
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Thread start(@NotNull Runnable task) {
|
||||
Objects.requireNonNull(task, "The task to start a virtual thread cannot be null");
|
||||
try {
|
||||
return (Thread) this.Thread_Builder_start_method.invoke(this.Thread_ofVirtual_method.invoke(null), task);
|
||||
} catch (Exception e) {
|
||||
// This should not be possible because it was tested in create()
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return A functional {@link ReflectionVirtualThreadService}.
|
||||
* @throws Throwable If creating virtual threads via reflection is not supported by the current runtime.
|
||||
* This could be any {@link Throwable}, including an {@link Exception} or an {@link Error}.
|
||||
*/
|
||||
static @NotNull ReflectionVirtualThreadService create() throws Throwable {
|
||||
// This will already throw something if the reflection fails
|
||||
var service = new ReflectionVirtualThreadService();
|
||||
// Run some tests to verify
|
||||
service.runTest();
|
||||
// If we end up here, it works
|
||||
return service;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
// Gale - virtual thread support
|
||||
|
||||
package org.galemc.gale.virtualthread;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.concurrent.ThreadFactory;
|
||||
|
||||
/**
|
||||
* An abstract service to create virtual threads.
|
||||
*
|
||||
* @author Martijn Muijsers
|
||||
*/
|
||||
public sealed abstract class VirtualThreadService permits ReflectionVirtualThreadService, DirectVirtualThreadService {
|
||||
|
||||
/**
|
||||
* @return A {@link ThreadFactory} that produces virtual threads.
|
||||
*/
|
||||
public abstract @NotNull ThreadFactory createFactory();
|
||||
|
||||
/**
|
||||
* @param task The runnable for the thread to execute.
|
||||
* @return A virtual thread that has been started with the given task.
|
||||
*/
|
||||
public abstract @NotNull Thread start(Runnable task);
|
||||
|
||||
/**
|
||||
* Runs a test on the {@link #createFactory} and {@link #start} methods,
|
||||
* which certainly throws some {@link Throwable} if something goes wrong.
|
||||
*/
|
||||
protected void runTest() throws Throwable {
|
||||
// This will definitely throw something if it doesn't work
|
||||
try {
|
||||
this.start(() -> {}).join();
|
||||
} catch (InterruptedException ignored) {} // Except InterruptedException, we don't care about that one
|
||||
try {
|
||||
var thread = this.createFactory().newThread(() -> {});
|
||||
thread.start();
|
||||
thread.join();
|
||||
} catch (InterruptedException ignored) {} // Except InterruptedException, we don't care about that one
|
||||
// If we end up here, it works
|
||||
}
|
||||
|
||||
private static boolean initialized = false;
|
||||
|
||||
/**
|
||||
* The {@link VirtualThreadService} for the current runtime,
|
||||
* or null if virtual threads are not supported, or if not {@link #initialized} yet.
|
||||
*/
|
||||
private static @Nullable VirtualThreadService implementation;
|
||||
|
||||
/**
|
||||
* @return Whether virtual threads are supported on the current runtime.
|
||||
*/
|
||||
public static boolean isSupported() {
|
||||
return get() != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The {@link VirtualThreadService} for the current runtime,
|
||||
* or null if virtual threads are not {@linkplain #isSupported() supported}.
|
||||
*
|
||||
* This method is thread-safe only after the first time it has been fully run.
|
||||
*/
|
||||
public static @Nullable VirtualThreadService get() {
|
||||
if (!initialized) {
|
||||
initialized = true;
|
||||
try {
|
||||
implementation = DirectVirtualThreadService.create();
|
||||
} catch (Throwable ignored) {
|
||||
try {
|
||||
implementation = ReflectionVirtualThreadService.create();
|
||||
} catch (Throwable ignored2) {}
|
||||
}
|
||||
}
|
||||
return implementation;
|
||||
}
|
||||
|
||||
/**
|
||||
* The minimum major version of Java that is known to support using virtual threads
|
||||
* (although possibly behind a feature preview flag).
|
||||
*/
|
||||
public static final int minimumJavaMajorVersionWithFeaturePreview = 19;
|
||||
|
||||
/**
|
||||
* The minimum major version of Java that is known to support using virtual threads
|
||||
* even without any feature preview flags.
|
||||
*/
|
||||
public static final int minimumJavaMajorVersionWithoutFeaturePreview = 21;
|
||||
|
||||
public static int getJavaMajorVersion() {
|
||||
var version = System.getProperty("java.version");
|
||||
if (version.startsWith("1.")) {
|
||||
return version.charAt(2) - '0';
|
||||
}
|
||||
if (version.contains("-")) {
|
||||
version = version.substring(0, version.indexOf("-"));
|
||||
}
|
||||
|
||||
int dotIndex = version.indexOf(".");
|
||||
return Integer.parseInt(dotIndex == -1 ? version : version.substring(0, dotIndex));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user