Signed-off-by: Minecon724 <git@m724.eu>
This commit is contained in:
parent
b421b48e51
commit
7cd334f4a2
8 changed files with 389 additions and 1 deletions
|
@ -27,6 +27,7 @@ import eu.m724.tweaks.module.redstone.RedstoneModule;
|
||||||
import eu.m724.tweaks.module.sleep.SleepModule;
|
import eu.m724.tweaks.module.sleep.SleepModule;
|
||||||
import eu.m724.tweaks.module.swing.SwingModule;
|
import eu.m724.tweaks.module.swing.SwingModule;
|
||||||
import eu.m724.tweaks.module.updater.UpdaterModule;
|
import eu.m724.tweaks.module.updater.UpdaterModule;
|
||||||
|
import eu.m724.tweaks.module.wordcoords.WordCoordsModule;
|
||||||
import eu.m724.tweaks.module.worldborder.WorldBorderExpandModule;
|
import eu.m724.tweaks.module.worldborder.WorldBorderExpandModule;
|
||||||
import eu.m724.tweaks.module.worldborder.WorldBorderHideModule;
|
import eu.m724.tweaks.module.worldborder.WorldBorderHideModule;
|
||||||
|
|
||||||
|
@ -157,6 +158,8 @@ public class TweaksPlugin extends MStatsPlugin {
|
||||||
|
|
||||||
TweaksModule.init(DurabilityModule.class);
|
TweaksModule.init(DurabilityModule.class);
|
||||||
|
|
||||||
|
TweaksModule.init(WordCoordsModule.class);
|
||||||
|
|
||||||
/* end modules */
|
/* end modules */
|
||||||
|
|
||||||
if (config.metrics()) {
|
if (config.metrics()) {
|
||||||
|
|
|
@ -0,0 +1,36 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2025 Minecon724
|
||||||
|
* Tweaks724 is licensed under the GNU General Public License. See the LICENSE.md file
|
||||||
|
* in the project root for the full license text.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package eu.m724.tweaks.module.wordcoords;
|
||||||
|
|
||||||
|
import eu.m724.tweaks.DebugLogger;
|
||||||
|
import eu.m724.tweaks.module.wordcoords.converter.Decoder;
|
||||||
|
import eu.m724.tweaks.module.wordcoords.converter.Encoder;
|
||||||
|
|
||||||
|
import java.util.NoSuchElementException;
|
||||||
|
|
||||||
|
public class WordCoordsConverter {
|
||||||
|
private final Encoder encoder;
|
||||||
|
private final Decoder decoder;
|
||||||
|
|
||||||
|
|
||||||
|
public WordCoordsConverter(WordList wordList) {
|
||||||
|
this.encoder = new Encoder(wordList);
|
||||||
|
this.decoder = new Decoder(wordList);
|
||||||
|
|
||||||
|
DebugLogger.fine("Words: %d (%d bits)", wordList.getWordCount(), wordList.getBitsPerWord());
|
||||||
|
DebugLogger.fine("Bits per word: %d", wordList.getBitsPerWord());
|
||||||
|
}
|
||||||
|
|
||||||
|
public String[] encode(int x, int z) {
|
||||||
|
return encoder.encode(x, z);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int[] decode(String[] words) throws NoSuchElementException {
|
||||||
|
return decoder.decode(words);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,118 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2025 Minecon724
|
||||||
|
* Tweaks724 is licensed under the GNU General Public License. See the LICENSE.md file
|
||||||
|
* in the project root for the full license text.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package eu.m724.tweaks.module.wordcoords;
|
||||||
|
|
||||||
|
import eu.m724.tweaks.Language;
|
||||||
|
import eu.m724.tweaks.module.TweaksModule;
|
||||||
|
import net.md_5.bungee.api.ChatColor;
|
||||||
|
import org.bukkit.command.Command;
|
||||||
|
import org.bukkit.command.CommandExecutor;
|
||||||
|
import org.bukkit.command.CommandSender;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class WordCoordsModule extends TweaksModule implements CommandExecutor {
|
||||||
|
private WordList wordList;
|
||||||
|
private WordCoordsConverter converter;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onInit() {
|
||||||
|
try {
|
||||||
|
this.wordList = WordList.fromFile(getPlugin().getDataFolder().toPath().resolve("storage/wordlist.txt"));
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.converter = new WordCoordsConverter(wordList);
|
||||||
|
|
||||||
|
registerCommand("wordcoords", this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
|
||||||
|
int x = 0, z = 0;
|
||||||
|
String[] words = new String[0];
|
||||||
|
|
||||||
|
boolean encode = false; // means encode pos to words
|
||||||
|
|
||||||
|
if (args.length == 0) {
|
||||||
|
if (!(sender instanceof Player player)) {
|
||||||
|
sender.sendMessage(Language.getString("wordCoordsPlayerOnly"));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
x = player.getLocation().getBlockX();
|
||||||
|
z = player.getLocation().getBlockZ();
|
||||||
|
|
||||||
|
encode = true;
|
||||||
|
} else {
|
||||||
|
if (args.length > 1) {
|
||||||
|
try {
|
||||||
|
double dx = Double.parseDouble(args[0]);
|
||||||
|
double dz = Double.parseDouble(args[args.length > 2 ? 2 : 1]);
|
||||||
|
|
||||||
|
if (dx > Integer.MAX_VALUE || dx < Integer.MIN_VALUE || dz > Integer.MAX_VALUE || dz < Integer.MIN_VALUE) {
|
||||||
|
sender.spigot().sendMessage(Language.getComponent("wordCoordsOutOfRange", ChatColor.RED));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
x = (int) dx;
|
||||||
|
z = (int) dz;
|
||||||
|
|
||||||
|
encode = true;
|
||||||
|
} catch (NumberFormatException ignored) { }
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!encode) {
|
||||||
|
String strArgs = String.join(" ", args);
|
||||||
|
words = smartDetectWords(strArgs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (encode) {
|
||||||
|
words = converter.encode(x, z);
|
||||||
|
|
||||||
|
sender.sendMessage("%d, %d encodes to ///%s".formatted(x, z, String.join(".", words)));
|
||||||
|
} else {
|
||||||
|
int[] xz = converter.decode(words);
|
||||||
|
x = xz[0];
|
||||||
|
z = xz[1];
|
||||||
|
|
||||||
|
sender.sendMessage("///%s decodes to around %d, %d".formatted(String.join(".", words), x, z));
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String[] smartDetectWords(String str) {
|
||||||
|
List<String> words = new ArrayList<>();
|
||||||
|
StringBuilder currentWord = new StringBuilder();
|
||||||
|
|
||||||
|
for (int i=0; i<str.length(); i++) {
|
||||||
|
char c = str.charAt(i);
|
||||||
|
|
||||||
|
if (Character.isLetter(c)) {
|
||||||
|
currentWord.append(c);
|
||||||
|
} else {
|
||||||
|
if (!currentWord.isEmpty()) {
|
||||||
|
words.add(currentWord.toString());
|
||||||
|
currentWord.setLength(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!currentWord.isEmpty()) {
|
||||||
|
words.add(currentWord.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
return words.toArray(String[]::new);
|
||||||
|
}
|
||||||
|
}
|
63
src/main/java/eu/m724/tweaks/module/wordcoords/WordList.java
Normal file
63
src/main/java/eu/m724/tweaks/module/wordcoords/WordList.java
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2025 Minecon724
|
||||||
|
* Tweaks724 is licensed under the GNU General Public License. See the LICENSE.md file
|
||||||
|
* in the project root for the full license text.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package eu.m724.tweaks.module.wordcoords;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class WordList {
|
||||||
|
private final List<String> wordList;
|
||||||
|
private final int bitsPerWord;
|
||||||
|
|
||||||
|
public WordList(List<String> words) {
|
||||||
|
this.wordList = words;
|
||||||
|
this.bitsPerWord = 32 - Integer.numberOfLeadingZeros(words.size()) - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getWord(int index) {
|
||||||
|
return wordList.get(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getWordIndex(String word) {
|
||||||
|
return wordList.indexOf(word);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String[] getWords(int... indexes) {
|
||||||
|
return Arrays.stream(indexes)
|
||||||
|
.mapToObj(this::getWord)
|
||||||
|
.toArray(String[]::new);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int[] getIndexes(String... words) {
|
||||||
|
return Arrays.stream(words)
|
||||||
|
.mapToInt(wordList::indexOf)
|
||||||
|
.toArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getWordCount() {
|
||||||
|
return wordList.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getBitsPerWord() {
|
||||||
|
return bitsPerWord;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static WordList fromFile(Path path) throws IOException {
|
||||||
|
try (var lines = Files.lines(path)) {
|
||||||
|
var list = lines.filter(s -> !s.isBlank())
|
||||||
|
.map(String::toLowerCase)
|
||||||
|
.distinct()
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
return new WordList(list);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,76 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2025 Minecon724
|
||||||
|
* Tweaks724 is licensed under the GNU General Public License. See the LICENSE.md file
|
||||||
|
* in the project root for the full license text.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package eu.m724.tweaks.module.wordcoords.converter;
|
||||||
|
|
||||||
|
import eu.m724.tweaks.DebugLogger;
|
||||||
|
import eu.m724.tweaks.module.wordcoords.WordList;
|
||||||
|
|
||||||
|
import java.util.NoSuchElementException;
|
||||||
|
|
||||||
|
public class Decoder {
|
||||||
|
private final WordList wordList;
|
||||||
|
private final int bitsPerWord;
|
||||||
|
|
||||||
|
public Decoder(WordList wordList) {
|
||||||
|
this.wordList = wordList;
|
||||||
|
this.bitsPerWord = wordList.getBitsPerWord();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int[] decode(String[] words) throws NoSuchElementException {
|
||||||
|
int[] wordIndexes = new int[words.length];
|
||||||
|
|
||||||
|
for (int i=0; i<words.length; i++) {
|
||||||
|
wordIndexes[i] = wordList.getWordIndex(words[i]);
|
||||||
|
if (wordIndexes[i] == -1)
|
||||||
|
throw new NoSuchElementException(words[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return decode(wordIndexes);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int[] decode(int[] wordIndexes) {
|
||||||
|
int bitsRequired = wordIndexes.length * wordList.getBitsPerWord();
|
||||||
|
int bitsRequiredPerCoordinate = bitsRequired / 2;
|
||||||
|
DebugLogger.finer("Bits required: %d (per coord: %d)", bitsRequired, bitsRequiredPerCoordinate);
|
||||||
|
|
||||||
|
|
||||||
|
long combinedValue = wordIndexesToCombinedValue(wordIndexes);
|
||||||
|
DebugLogger.finer("Combined value: %d", combinedValue);
|
||||||
|
|
||||||
|
|
||||||
|
int[] decodedCoords = decodeCoords(combinedValue, bitsRequiredPerCoordinate);
|
||||||
|
int xCoord = decodedCoords[0];
|
||||||
|
int zCoord = decodedCoords[1];
|
||||||
|
DebugLogger.info("Chunk: %d, %d", xCoord, zCoord);
|
||||||
|
|
||||||
|
return new int[] { xCoord * 16 + 8, zCoord * 16 + 8 }; // +8 to make it center of chunk
|
||||||
|
}
|
||||||
|
|
||||||
|
private long wordIndexesToCombinedValue(int[] wordIndexes) {
|
||||||
|
long combinedValue = 0;
|
||||||
|
|
||||||
|
for (int i=0; i<wordIndexes.length; i++) {
|
||||||
|
combinedValue |= wordIndexes[i];
|
||||||
|
combinedValue <<= bitsPerWord;
|
||||||
|
}
|
||||||
|
|
||||||
|
combinedValue >>= bitsPerWord;
|
||||||
|
|
||||||
|
return combinedValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int[] decodeCoords(long combinedValue, int bitsRequiredPerCoordinate) {
|
||||||
|
int coordinateMask = (1 << bitsRequiredPerCoordinate) - 1;
|
||||||
|
int coordinateOffset = 1 << (bitsRequiredPerCoordinate - 1);
|
||||||
|
|
||||||
|
int z = (int) (combinedValue & coordinateMask) - coordinateOffset;
|
||||||
|
int x = (int) (combinedValue >> bitsRequiredPerCoordinate) - coordinateOffset;
|
||||||
|
|
||||||
|
return new int[] { x, z };
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,82 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2025 Minecon724
|
||||||
|
* Tweaks724 is licensed under the GNU General Public License. See the LICENSE.md file
|
||||||
|
* in the project root for the full license text.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package eu.m724.tweaks.module.wordcoords.converter;
|
||||||
|
|
||||||
|
import eu.m724.tweaks.DebugLogger;
|
||||||
|
import eu.m724.tweaks.module.wordcoords.WordList;
|
||||||
|
|
||||||
|
public class Encoder {
|
||||||
|
private final WordList wordList;
|
||||||
|
private final int bitsPerWord;
|
||||||
|
|
||||||
|
public Encoder(WordList wordList) {
|
||||||
|
this.wordList = wordList;
|
||||||
|
this.bitsPerWord = wordList.getBitsPerWord();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String[] encode(int xCoord, int zCoord) {
|
||||||
|
xCoord = Math.floorDiv(xCoord, 16);
|
||||||
|
zCoord = Math.floorDiv(zCoord, 16);
|
||||||
|
DebugLogger.finer("Chunk: %d, %d", xCoord, zCoord);
|
||||||
|
|
||||||
|
|
||||||
|
int wordsRequired = findWordsRequired(xCoord, zCoord);
|
||||||
|
DebugLogger.finer("Words required: %d", wordsRequired);
|
||||||
|
|
||||||
|
int bitsRequired = wordsRequired * bitsPerWord;
|
||||||
|
int bitsRequiredPerCoordinate = bitsRequired / 2;
|
||||||
|
DebugLogger.finer("Bits required: %d (per coord: %d)", bitsRequired, bitsRequiredPerCoordinate);
|
||||||
|
|
||||||
|
|
||||||
|
int encodedX = encodeCoord(xCoord, bitsRequiredPerCoordinate);
|
||||||
|
int encodedZ = encodeCoord(zCoord, bitsRequiredPerCoordinate);
|
||||||
|
DebugLogger.finer("Encoded coordinates: %d, %d", encodedX, encodedZ);
|
||||||
|
|
||||||
|
|
||||||
|
long combinedValue = ((long) encodedX << bitsRequiredPerCoordinate) | encodedZ;
|
||||||
|
DebugLogger.finer("Combined value: %d", combinedValue);
|
||||||
|
|
||||||
|
|
||||||
|
int[] wordIndexes = combinedValueToWordIndexes(combinedValue, wordsRequired);
|
||||||
|
return wordList.getWords(wordIndexes);
|
||||||
|
}
|
||||||
|
|
||||||
|
private int findBitsRequiredPerCoordinate(int x, int z) {
|
||||||
|
int n = Math.max(Math.abs(x), Math.abs(z));
|
||||||
|
return n == 0 ? 1 : 32 - Integer.numberOfLeadingZeros(n);
|
||||||
|
}
|
||||||
|
|
||||||
|
private int findWordsRequired(int x, int z) {
|
||||||
|
int brpc = findBitsRequiredPerCoordinate(x, z);
|
||||||
|
return Math.ceilDiv(brpc * 2, wordList.getBitsPerWord());
|
||||||
|
}
|
||||||
|
|
||||||
|
private int encodeCoord(int coord, int bitsRequiredPerCoordinate) {
|
||||||
|
// Bitmask and offset for positive integer conversion
|
||||||
|
int coordinateMask = (1 << bitsRequiredPerCoordinate) - 1;
|
||||||
|
int coordinateOffset = 1 << (bitsRequiredPerCoordinate - 1);
|
||||||
|
|
||||||
|
// Encode coordinates with offset into positive range
|
||||||
|
return (coordinateOffset + coord) & coordinateMask;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int[] combinedValueToWordIndexes(long combinedValue, int wordsRequired) {
|
||||||
|
int bitsRequired = wordsRequired * bitsPerWord;
|
||||||
|
|
||||||
|
// Break into word indexes
|
||||||
|
int[] wordIndexes = new int[wordsRequired];
|
||||||
|
int currentIndex = wordsRequired; // Start filling from end of array
|
||||||
|
|
||||||
|
for (int remainingBits = bitsRequired; remainingBits > 0; remainingBits -= bitsPerWord) {
|
||||||
|
int wordMask = (1 << bitsPerWord) - 1;
|
||||||
|
wordIndexes[--currentIndex] = (int) (combinedValue & wordMask);
|
||||||
|
combinedValue >>= bitsPerWord;
|
||||||
|
}
|
||||||
|
|
||||||
|
return wordIndexes;
|
||||||
|
}
|
||||||
|
}
|
|
@ -43,6 +43,10 @@ commands:
|
||||||
durabilityalert:
|
durabilityalert:
|
||||||
description: Durability alert toggle
|
description: Durability alert toggle
|
||||||
permission: tweaks724.durabilityalert
|
permission: tweaks724.durabilityalert
|
||||||
|
wordcoords:
|
||||||
|
description: Word to coords conversion
|
||||||
|
permission: tweaks724.wordcoords
|
||||||
|
aliases: [woco, wc, w3w]
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
tweaks724.chatmanage:
|
tweaks724.chatmanage:
|
||||||
|
@ -63,6 +67,8 @@ permissions:
|
||||||
default: false
|
default: false
|
||||||
tweaks724.durabilityalert:
|
tweaks724.durabilityalert:
|
||||||
default: true
|
default: true
|
||||||
|
tweaks724.wordcoords:
|
||||||
|
default: true
|
||||||
|
|
||||||
7weaks724.ignore.this:
|
7weaks724.ignore.this:
|
||||||
description: "Internal, not for use. ${project.spigot.version}"
|
description: "Internal, not for use. ${project.spigot.version}"
|
||||||
|
|
|
@ -39,3 +39,7 @@ clickToExecuteCommand = Click to execute command
|
||||||
|
|
||||||
durabilityEnabled = Enabled durability alert
|
durabilityEnabled = Enabled durability alert
|
||||||
durabilityDisabled = Disabled durability alert
|
durabilityDisabled = Disabled durability alert
|
||||||
|
|
||||||
|
# When console executes /wordcoords without arguments
|
||||||
|
wordCoordsPlayerOnly = Only players can execute this command without arguments.
|
||||||
|
wordCoordsOutOfRange = Those coordinates are invalid.
|
Loading…
Add table
Reference in a new issue