Push forward

This commit is contained in:
2026-07-14 13:29:52 +08:00
parent 969cd5c5f8
commit dd3efffa76
246 changed files with 69 additions and 69 deletions
@@ -0,0 +1,53 @@
/*
* This file is part of Pufferfish (https://github.com/pufferfish-gg/Pufferfish)
*
* Pufferfish is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Pufferfish is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Pufferfish. If not, see <https://www.gnu.org/licenses/>.
*/
package gg.pufferfish.pufferfish.simd;
import jdk.incubator.vector.FloatVector;
import jdk.incubator.vector.IntVector;
import jdk.incubator.vector.VectorSpecies;
import org.slf4j.Logger;
/**
* Basically, java is annoying, and we have to push this out to its own class.
*/
@Deprecated
public class SIMDChecker {
@Deprecated
public static boolean canEnable(Logger logger) {
try {
SIMDDetection.testRun = true;
VectorSpecies<Integer> ISPEC = IntVector.SPECIES_PREFERRED;
VectorSpecies<Float> FSPEC = FloatVector.SPECIES_PREFERRED;
logger.info("Max SIMD vector size on this system is {} bits (int)", ISPEC.vectorBitSize());
logger.info("Max SIMD vector size on this system is " + FSPEC.vectorBitSize() + " bits (float)");
if (ISPEC.elementSize() < 2 || FSPEC.elementSize() < 2) {
logger.warn("SIMD is not properly supported on this system!");
return false;
}
return true;
} catch (NoClassDefFoundError | Exception ignored) {
} // Basically, we don't do anything. This lets us detect if it's not functional and disable it.
return false;
}
}
@@ -0,0 +1,53 @@
/*
* This file is part of Pufferfish (https://github.com/pufferfish-gg/Pufferfish)
*
* Pufferfish is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Pufferfish is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Pufferfish. If not, see <https://www.gnu.org/licenses/>.
*/
package gg.pufferfish.pufferfish.simd;
import org.slf4j.Logger;
@Deprecated
public class SIMDDetection {
public static boolean isEnabled = false;
public static boolean testRun = false;
@Deprecated
public static boolean canEnable(Logger logger) {
try {
return SIMDChecker.canEnable(logger);
} catch (NoClassDefFoundError | Exception ignored) {
return false;
}
}
@Deprecated
public static int getJavaVersion() {
// https://stackoverflow.com/a/2591122
String version = System.getProperty("java.version");
if (version.startsWith("1.")) {
version = version.substring(2, 3);
} else {
int dot = version.indexOf(".");
if (dot != -1) {
version = version.substring(0, dot);
}
}
version = version.split("-")[0]; // Azul is stupid
return Integer.parseInt(version);
}
}
@@ -0,0 +1,101 @@
/*
* This file is part of Pufferfish (https://github.com/pufferfish-gg/Pufferfish)
*
* Pufferfish is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Pufferfish is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Pufferfish. If not, see <https://www.gnu.org/licenses/>.
*/
package gg.pufferfish.pufferfish.simd;
import jdk.incubator.vector.FloatVector;
import jdk.incubator.vector.IntVector;
import jdk.incubator.vector.VectorMask;
import jdk.incubator.vector.VectorSpecies;
import org.bukkit.map.MapPalette;
import java.awt.*;
@Deprecated
public class VectorMapPalette {
private static final VectorSpecies<Integer> I_SPEC = IntVector.SPECIES_PREFERRED;
private static final VectorSpecies<Float> F_SPEC = FloatVector.SPECIES_PREFERRED;
@Deprecated
public static void matchColorVectorized(int[] in, byte[] out) {
int speciesLength = I_SPEC.length();
int i;
for (i = 0; i < in.length - speciesLength; i += speciesLength) {
float[] redsArr = new float[speciesLength];
float[] bluesArr = new float[speciesLength];
float[] greensArr = new float[speciesLength];
int[] alphasArr = new int[speciesLength];
for (int j = 0; j < speciesLength; j++) {
alphasArr[j] = (in[i + j] >> 24) & 0xFF;
redsArr[j] = (in[i + j] >> 16) & 0xFF;
greensArr[j] = (in[i + j] >> 8) & 0xFF;
bluesArr[j] = (in[i + j] >> 0) & 0xFF;
}
IntVector alphas = IntVector.fromArray(I_SPEC, alphasArr, 0);
FloatVector reds = FloatVector.fromArray(F_SPEC, redsArr, 0);
FloatVector greens = FloatVector.fromArray(F_SPEC, greensArr, 0);
FloatVector blues = FloatVector.fromArray(F_SPEC, bluesArr, 0);
IntVector resultIndex = IntVector.zero(I_SPEC);
VectorMask<Integer> modificationMask = VectorMask.fromLong(I_SPEC, 0xffffffff);
modificationMask = modificationMask.and(alphas.lt(128).not());
FloatVector bestDistances = FloatVector.broadcast(F_SPEC, Float.MAX_VALUE);
for (int c = 4; c < MapPalette.colors.length; c++) {
// We're using 32-bit floats here because it's 2x faster and nobody will know the difference.
// For correctness, the original algorithm uses 64-bit floats instead. Completely unnecessary.
FloatVector compReds = FloatVector.broadcast(F_SPEC, MapPalette.colors[c].getRed());
FloatVector compGreens = FloatVector.broadcast(F_SPEC, MapPalette.colors[c].getGreen());
FloatVector compBlues = FloatVector.broadcast(F_SPEC, MapPalette.colors[c].getBlue());
FloatVector rMean = reds.add(compReds).div(2.0f);
FloatVector rDiff = reds.sub(compReds);
FloatVector gDiff = greens.sub(compGreens);
FloatVector bDiff = blues.sub(compBlues);
FloatVector weightR = rMean.div(256.0f).add(2);
FloatVector weightG = FloatVector.broadcast(F_SPEC, 4.0f);
FloatVector weightB = FloatVector.broadcast(F_SPEC, 255.0f).sub(rMean).div(256.0f).add(2.0f);
FloatVector distance = weightR.mul(rDiff).mul(rDiff).add(weightG.mul(gDiff).mul(gDiff)).add(weightB.mul(bDiff).mul(bDiff));
// Now we compare to the best distance we've found.
// This mask contains a "1" if better, and a "0" otherwise.
VectorMask<Float> bestDistanceMask = distance.lt(bestDistances);
bestDistances = bestDistances.blend(distance, bestDistanceMask); // Update the best distances
// Update the result array
// We also AND with the modification mask because we don't want to interfere if the alpha value isn't large enough.
resultIndex = resultIndex.blend(c, bestDistanceMask.cast(I_SPEC).and(modificationMask)); // Update the results
}
for (int j = 0; j < speciesLength; j++) {
int index = resultIndex.lane(j);
out[i + j] = (byte) (index < 128 ? index : -129 + (index - 127));
}
}
// For the final ones, fall back to the regular method
for (; i < in.length; i++) {
out[i] = MapPalette.matchColor(new Color(in[i], true));
}
}
}
@@ -0,0 +1,11 @@
package io.nanachiyo0721.shiroha.api.config;
import org.jspecify.annotations.Nullable;
public record ConfigDataPair(
String key,
Object value,
@Nullable String comment,
@Nullable String[] suggestions
) {
}
@@ -0,0 +1,82 @@
package io.nanachiyo0721.shiroha.api.config;
import org.jetbrains.annotations.NotNull;
import java.io.File;
/**
* Builder interface for creating {@link ShirohaConfigsInstance} instances.
* <p>
* Provides multiple overloaded factory methods to construct configuration instances
* with varying levels of customization, including custom base directories,
* file names, and command names.
*/
public interface ShirohaConfigBuilder {
/**
* Creates a configuration instance using the default base directory and file name.
*
* @param loader the class loader used to load configuration resources
* @param name the configuration name identifier
* @param pack the package path where configuration classes are located
* @return a new {@link ShirohaConfigsInstance}
*/
ShirohaConfigsInstance of(
@NotNull ClassLoader loader,
@NotNull String name,
@NotNull String pack
);
/**
* Creates a configuration instance with a custom base directory.
*
* @param loader the class loader used to load configuration resources
* @param base the base directory where the configuration file is stored
* @param name the configuration name identifier
* @param pack the package path where configuration classes are located
* @return a new {@link ShirohaConfigsInstance}
*/
ShirohaConfigsInstance of(
@NotNull ClassLoader loader,
@NotNull File base,
@NotNull String name,
@NotNull String pack
);
/**
* Creates a configuration instance with a custom base directory and file name.
*
* @param loader the class loader used to load configuration resources
* @param base the base directory where the configuration file is stored
* @param name the configuration name identifier
* @param file_name the configuration file name (without extension)
* @param pack the package path where configuration classes are located
* @return a new {@link ShirohaConfigsInstance}
*/
ShirohaConfigsInstance of(
@NotNull ClassLoader loader,
@NotNull File base,
@NotNull String name,
@NotNull String file_name,
@NotNull String pack
);
/**
* Creates a configuration instance with full customization options.
*
* @param loader the class loader used to load configuration resources
* @param base the base directory where the configuration file is stored
* @param name the configuration name identifier
* @param file_name the configuration file name (without extension)
* @param command_name the command name associated with this configuration
* @param pack the package path where configuration classes are located
* @return a new {@link ShirohaConfigsInstance}
*/
ShirohaConfigsInstance of(
@NotNull ClassLoader loader,
@NotNull File base,
@NotNull String name,
@NotNull String file_name,
@NotNull String command_name,
@NotNull String pack
);
}
@@ -0,0 +1,203 @@
package io.nanachiyo0721.shiroha.api.config;
import org.jetbrains.annotations.NotNull;
import java.io.IOException;
import java.util.List;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
/**
* Interface for managing configuration instances and operations.
* Provides methods for loading, saving, modifying, and querying configuration values.
*/
public interface ShirohaConfigsInstance {
/**
* Initialize the configuration subsystem for this instance.
* Implementations should load configuration files, apply defaults and
* prepare any internal caches or state needed for subsequent operations.
* This method may perform I/O and therefore can throw an {@link IOException}
* if initialization fails.
*
* @throws IOException if an I/O error occurs while initializing
*/
void initialize() throws IOException;
/**
* Reloads all configurations asynchronously
*
* @param keepComments whether to preserve existing comments in the config file
* @return CompletableFuture that completes when reload is finished
*/
@NotNull CompletableFuture<Void> reloadAsync(boolean keepComments);
/**
* Sets a configuration value by key
*
* @param key the configuration key (dot-separated path)
* @param value the value to set
* @return true if the key exists and value was set, false otherwise
*/
boolean setConfig(String key, Object value);
/**
* Saves all pending configuration changes to disk
*/
void saveConfigs();
/**
* Resets a configuration value to its default
*
* @param key the configuration key to reset
*/
void resetConfig(String key);
/**
* Gets the default value of a configuration as string
*
* @param key the configuration key
* @return the default value as string
*/
String getDefaultConfig(String key);
/**
* Gets the current value of a configuration as string
*
* @param key the configuration key
* @return the current value as string
*/
String getConfig(String key);
/**
* Gets the original (untransformed) value of a configuration
*
* @param key the configuration key
* @param <T> the expected type of the value
* @return the original configuration value
*/
<T> T getConfigOrigin(String key);
/**
* Gets available suggestions for a configuration key
*
* @param key the configuration key
* @return array of suggestion strings, or null if no suggestions available
*/
String[] getConfigSuggestions(String key);
/**
* Completes a partial configuration path by finding matching keys
*
* @param partialPath the partial path to complete
* @return list of possible completions
*/
List<String> completeConfigPath(String partialPath);
/**
* Completes a partial configuration path with specific depth
*
* @param partialPath the partial path to complete
* @param dotIndex the maximum number of dots (depth) in the result
* @return list of possible completions
*/
List<String> completeConfigPath(String partialPath, int dotIndex);
/**
* Gets all configuration paths that start with the given prefix
*
* @param currentPath the prefix to search for
* @return list of matching configuration paths
*/
List<String> getAllConfigPaths(String currentPath);
/**
* Gets all configuration data pairs without prefix filter
*
* @return set of all configuration data
*/
Set<ConfigDataPair> getAllData();
/**
* Gets configuration data pairs filtered by prefix
*
* @param prefix the prefix to filter by
* @return set of matching configuration data
*/
Set<ConfigDataPair> getData(String prefix);
/**
* Gets all configuration data pairs with full information (comments and suggestions)
*
* @return set of all configuration data with full details
*/
Set<ConfigDataPair> getAllDataFull();
/**
* Gets configuration data pairs with full information filtered by prefix
*
* @param prefix the prefix to filter by
* @return set of matching configuration data with full details
*/
Set<ConfigDataPair> getDataFull(String prefix);
/**
* Gets configuration data pairs with optional comments and suggestions
*
* @param prefix the prefix to filter by
* @param _comment whether to include comments
* @param _withSuggestions whether to include suggestions
* @return set of matching configuration data
*/
Set<ConfigDataPair> getData(String prefix, boolean _comment, boolean _withSuggestions);
/**
* Gets configuration data pairs for specified keys
*
* @param list list of configuration keys
* @return set of configuration data for the specified keys
*/
Set<ConfigDataPair> getData(List<String> list);
/**
* Gets configuration data pairs with comments for specified keys
*
* @param list list of configuration keys
* @return set of configuration data with comments
*/
Set<ConfigDataPair> getDataWithComment(List<String> list);
/**
* Gets configuration data pairs with full information for specified keys
*
* @param list list of configuration keys
* @return set of configuration data with full details
*/
Set<ConfigDataPair> getDataFull(List<String> list);
/**
* Gets configuration data pairs for specified keys with optional features
*
* @param list list of configuration keys
* @param _comment whether to include comments
* @param _withSuggestions whether to include suggestions
* @return set of configuration data
*/
Set<ConfigDataPair> getData(List<String> list, boolean _comment, boolean _withSuggestions);
/**
* Remove a configuration entry by its key.
* If the key does not exist this should be a no-op.
*
* @param key the configuration key to remove
*/
void removeConfig(String key);
/**
* Remove multiple configuration entries by their keys.
* Implementations should attempt to remove each key provided. If some
* keys do not exist they may be ignored.
*
* @param keys an array of configuration keys to remove
*/
void removeConfig(String[] keys);
}