forked from SteamWar/SteamWar
Compare commits
102
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f63390915b | ||
|
|
db939e27d9 | ||
|
|
36e6329f2b | ||
|
|
b2300c128e | ||
|
|
9cb4e0b7db | ||
|
|
8e4730bd12 | ||
|
|
baa278a1f5 | ||
|
|
8fd6fdf81e | ||
|
|
7d0f5aceae | ||
|
|
ad9087c038 | ||
|
|
872da5f62a | ||
|
|
e830d087c9 | ||
|
|
b4661ea345 | ||
|
|
4e5945d2bc | ||
|
|
a5b8f00c58 | ||
|
|
8e23490b04 | ||
|
|
4c6b5e654a | ||
|
|
38a33b7298 | ||
|
|
32797bd2a8 | ||
|
|
28b62bf27b | ||
|
|
db915f7be7 | ||
|
|
aef26b5e9f | ||
|
|
8009098965 | ||
|
|
47f0d9858b | ||
|
|
c3eda01e20 | ||
|
|
5e81081620 | ||
|
|
ed70d65bc4 | ||
|
|
2ce8799a19 | ||
|
|
15036a7ba0 | ||
|
|
d77c014409 | ||
|
|
6b5441bb04 | ||
|
|
54d30f4a42 | ||
|
|
e41f76937e | ||
|
|
0c7ee052e8 | ||
|
|
b60cdfc88d | ||
|
|
98f04451a4 | ||
|
|
c95901bc42 | ||
|
|
211e2693cf | ||
|
|
1980375e73 | ||
|
|
ef041272bb | ||
|
|
bfbf98c1d6 | ||
|
|
c4b46e4431 | ||
|
|
c238b9bdf2 | ||
|
|
fec4007120 | ||
|
|
11798c3ec0 | ||
|
|
9935ab785d | ||
|
|
8e3e71dd96 | ||
|
|
3fe2e006b9 | ||
|
|
9d5fcc07e8 | ||
|
|
0c3e63d521 | ||
|
|
27d260a8d2 | ||
|
|
37f017b84c | ||
|
|
21d0a4359d | ||
|
|
8318d408b6 | ||
|
|
d14ddc4fab | ||
|
|
5cc5529533 | ||
|
|
380693a2b7 | ||
|
|
69aef9dfb6 | ||
|
|
097df865e5 | ||
|
|
230f8c521a | ||
|
|
253a975c73 | ||
|
|
12dd46176b | ||
|
|
2111d72387 | ||
|
|
3aa471c3ab | ||
|
|
47557d7bb8 | ||
|
|
62850b2e11 | ||
|
|
7f47d687d3 | ||
|
|
362e1cebff | ||
|
|
39cacbd701 | ||
|
|
503261ce6d | ||
|
|
8a86ebde83 | ||
|
|
be915770b1 | ||
|
|
70c4225827 | ||
|
|
904a850a3f | ||
|
|
4c75879915 | ||
|
|
65cc6119b0 | ||
|
|
975d42c17f | ||
|
|
317cb0adf8 | ||
|
|
4266a7f35b | ||
|
|
26d5ff7aad | ||
|
|
b21ff415a8 | ||
|
|
9fe05b59e4 | ||
|
|
2302f9aafb | ||
|
|
d727982ce0 | ||
|
|
2b42f0d13c | ||
|
|
3e3a209c80 | ||
|
|
1d43021853 | ||
|
|
5a224c1bcc | ||
|
|
6330dbc2b1 | ||
|
|
4306fe7bc9 | ||
|
|
58df6087f0 | ||
|
|
b9b7869124 | ||
|
|
1e85b14667 | ||
|
|
35c27923aa | ||
|
|
c3b3e3e60f | ||
|
|
3aa1bf7ffd | ||
|
|
5a02a58437 | ||
|
|
6209b86149 | ||
|
|
6f1403df9c | ||
|
|
0113788da2 | ||
|
|
8a7f65e000 | ||
|
|
8401f67b24 |
@@ -7,8 +7,14 @@
|
|||||||
!gradle/wrapper/gradle-wrapper.jar
|
!gradle/wrapper/gradle-wrapper.jar
|
||||||
steamwar.properties
|
steamwar.properties
|
||||||
|
|
||||||
|
# Kotlin
|
||||||
|
.kotlin
|
||||||
|
|
||||||
# IntelliJ IDEA
|
# IntelliJ IDEA
|
||||||
*.iml
|
*.iml
|
||||||
|
.idea
|
||||||
|
!.idea/copyright
|
||||||
|
!.idea/runConfigurations
|
||||||
|
|
||||||
# VSCode
|
# VSCode
|
||||||
bin/
|
bin/
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ dependencies {
|
|||||||
compileOnly(libs.netty)
|
compileOnly(libs.netty)
|
||||||
|
|
||||||
implementation(libs.luaj)
|
implementation(libs.luaj)
|
||||||
implementation(files("$projectDir/../libs/YAPION-SNAPSHOT.jar"))
|
// implementation(files("$projectDir/../libs/YAPION-SNAPSHOT.jar"))
|
||||||
}
|
}
|
||||||
|
|
||||||
widener {
|
widener {
|
||||||
|
|||||||
@@ -20,8 +20,7 @@
|
|||||||
package de.steamwar.bausystem;
|
package de.steamwar.bausystem;
|
||||||
|
|
||||||
import de.steamwar.bausystem.config.BauServer;
|
import de.steamwar.bausystem.config.BauServer;
|
||||||
import de.steamwar.bausystem.configplayer.Config;
|
import de.steamwar.bausystem.config.ConfigStore;
|
||||||
import de.steamwar.bausystem.configplayer.ConfigConverter;
|
|
||||||
import de.steamwar.bausystem.features.gui.BauGUI;
|
import de.steamwar.bausystem.features.gui.BauGUI;
|
||||||
import de.steamwar.bausystem.features.script.lua.SteamWarLuaPlugin;
|
import de.steamwar.bausystem.features.script.lua.SteamWarLuaPlugin;
|
||||||
import de.steamwar.bausystem.features.script.lua.libs.LuaLib;
|
import de.steamwar.bausystem.features.script.lua.libs.LuaLib;
|
||||||
@@ -36,7 +35,6 @@ import de.steamwar.bausystem.region.Region;
|
|||||||
import de.steamwar.bausystem.region.RegionSystem;
|
import de.steamwar.bausystem.region.RegionSystem;
|
||||||
import de.steamwar.bausystem.utils.ScoreboardElement;
|
import de.steamwar.bausystem.utils.ScoreboardElement;
|
||||||
import de.steamwar.bausystem.utils.TickManager;
|
import de.steamwar.bausystem.utils.TickManager;
|
||||||
import de.steamwar.bausystem.worlddata.WorldData;
|
|
||||||
import de.steamwar.command.AbstractValidator;
|
import de.steamwar.command.AbstractValidator;
|
||||||
import de.steamwar.command.SWCommandUtils;
|
import de.steamwar.command.SWCommandUtils;
|
||||||
import de.steamwar.core.CRIUSleepEvent;
|
import de.steamwar.core.CRIUSleepEvent;
|
||||||
@@ -112,9 +110,6 @@ public class BauSystem extends JavaPlugin implements Listener {
|
|||||||
if (any instanceof PanzernAlgorithm) {
|
if (any instanceof PanzernAlgorithm) {
|
||||||
Panzern.add((PanzernAlgorithm) any);
|
Panzern.add((PanzernAlgorithm) any);
|
||||||
}
|
}
|
||||||
if (any instanceof ConfigConverter) {
|
|
||||||
Config.addConfigConverter((ConfigConverter) any);
|
|
||||||
}
|
|
||||||
if (any instanceof BoundingBoxLoader) {
|
if (any instanceof BoundingBoxLoader) {
|
||||||
((BoundingBoxLoader) any).load();
|
((BoundingBoxLoader) any).load();
|
||||||
}
|
}
|
||||||
@@ -160,9 +155,8 @@ public class BauSystem extends JavaPlugin implements Listener {
|
|||||||
@Override
|
@Override
|
||||||
public void onDisable() {
|
public void onDisable() {
|
||||||
linker.unlink();
|
linker.unlink();
|
||||||
WorldData.write();
|
|
||||||
RegionSystem.INSTANCE.save();
|
RegionSystem.INSTANCE.save();
|
||||||
Config.getInstance().saveAll();
|
ConfigStore.saveAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
private <T extends CommandSender> AbstractValidator<T, ?> validator(Permission permission) {
|
private <T extends CommandSender> AbstractValidator<T, ?> validator(Permission permission) {
|
||||||
|
|||||||
@@ -0,0 +1,47 @@
|
|||||||
|
/*
|
||||||
|
* This file is a part of the SteamWar software.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2025 SteamWar.de-Serverteam
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program 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 Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.steamwar.bausystem.config;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts a config of one version into the next version, e.g. ConfigV1 to
|
||||||
|
* ConfigV2. The converter for the second newest version converts into the
|
||||||
|
* current version. Converters are registered in the {@link ConfigStore}
|
||||||
|
* of the config.
|
||||||
|
*
|
||||||
|
* @param <F> the config version to convert from
|
||||||
|
* @param <T> the config version to convert to
|
||||||
|
*/
|
||||||
|
public interface ConfigConverter<F extends VersionedConfig, T extends VersionedConfig> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the version this converter converts from
|
||||||
|
*/
|
||||||
|
int version();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts the config to the next version. If anything goes wrong do not
|
||||||
|
* silently exit this method, throw an Exception. The {@link ConfigStore}
|
||||||
|
* will deal with it.
|
||||||
|
*
|
||||||
|
* @param config the config to convert
|
||||||
|
* @return the converted config
|
||||||
|
*/
|
||||||
|
T convert(F config);
|
||||||
|
}
|
||||||
@@ -0,0 +1,364 @@
|
|||||||
|
/*
|
||||||
|
* This file is a part of the SteamWar software.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2026 SteamWar.de-Serverteam
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program 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 Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.steamwar.bausystem.config;
|
||||||
|
|
||||||
|
import com.google.gson.Gson;
|
||||||
|
import com.google.gson.JsonObject;
|
||||||
|
import com.google.gson.JsonParser;
|
||||||
|
import de.steamwar.sql.UserConfig;
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.StandardCopyOption;
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.function.Function;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads, converts and saves a versioned config stored as JSON. Older versions
|
||||||
|
* are converted with the registered {@link ConfigConverter}s, legacy formats
|
||||||
|
* that are not JSON can be read with {@link #legacy(Predicate, Function)}.
|
||||||
|
* <p>
|
||||||
|
* A store can be bound to where the config is stored:
|
||||||
|
* <ul>
|
||||||
|
* <li>{@link #userConfig(String)}: one config per player in the {@link UserConfig}, use {@link #get(Player)}</li>
|
||||||
|
* <li>{@link #file(File)}: one config in a file, use {@link #get()}</li>
|
||||||
|
* <li>unbound: use {@link #load(File)} and {@link #save(File, VersionedConfig)}</li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* @param <T> the current config version
|
||||||
|
*/
|
||||||
|
public class ConfigStore<T extends VersionedConfig> {
|
||||||
|
|
||||||
|
private static final List<ConfigStore<?>> STORES = new ArrayList<>();
|
||||||
|
|
||||||
|
private final Class<T> type;
|
||||||
|
private final int version;
|
||||||
|
private final Supplier<T> defaultConfig;
|
||||||
|
|
||||||
|
private final Map<Integer, Class<? extends VersionedConfig>> jsonVersions = new HashMap<>();
|
||||||
|
private final Map<Integer, ConfigConverter<?, ?>> converters = new HashMap<>();
|
||||||
|
private final List<LegacyFormat> legacyFormats = new ArrayList<>();
|
||||||
|
private Gson gson = new Gson();
|
||||||
|
private Integer unversionedVersion = null;
|
||||||
|
|
||||||
|
private String userConfigKey = null;
|
||||||
|
private Predicate<T> removeIf = config -> false;
|
||||||
|
private final Map<UUID, T> playerConfigs = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
private File file = null;
|
||||||
|
private File legacyFile = null;
|
||||||
|
private Function<String, ? extends VersionedConfig> legacyFileReader = null;
|
||||||
|
private T fileConfig = null;
|
||||||
|
|
||||||
|
private record LegacyFormat(Predicate<String> matches, Function<String, ? extends VersionedConfig> reader) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public ConfigStore(Class<T> type, int version, Supplier<T> defaultConfig) {
|
||||||
|
this.type = type;
|
||||||
|
this.version = version;
|
||||||
|
this.defaultConfig = defaultConfig;
|
||||||
|
jsonVersions.put(version, type);
|
||||||
|
STORES.add(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ConfigStore<T> gson(Gson gson) {
|
||||||
|
this.gson = gson;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers an older config version that is stored as JSON.
|
||||||
|
*/
|
||||||
|
public ConfigStore<T> jsonVersion(int version, Class<? extends VersionedConfig> type) {
|
||||||
|
jsonVersions.put(version, type);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ConfigStore<T> converter(ConfigConverter<?, ?> converter) {
|
||||||
|
if (converters.putIfAbsent(converter.version(), converter) != null) {
|
||||||
|
throw new IllegalStateException("Duplicate converter for version " + converter.version() + " of " + type.getSimpleName());
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers a legacy format that is not JSON, stored strings matching the predicate are read by the reader.
|
||||||
|
*/
|
||||||
|
public ConfigStore<T> legacy(Predicate<String> matches, Function<String, ? extends VersionedConfig> reader) {
|
||||||
|
legacyFormats.add(new LegacyFormat(matches, reader));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The version of stored JSON without a version field.
|
||||||
|
*/
|
||||||
|
public ConfigStore<T> unversioned(int version) {
|
||||||
|
this.unversionedVersion = version;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Binds this store to the {@link UserConfig} with the given key, one config per player.
|
||||||
|
*/
|
||||||
|
public ConfigStore<T> userConfig(String key) {
|
||||||
|
this.userConfigKey = key;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes the config from the {@link UserConfig} instead of saving it, if the predicate matches.
|
||||||
|
*/
|
||||||
|
public ConfigStore<T> removeIf(Predicate<T> removeIf) {
|
||||||
|
this.removeIf = removeIf;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Binds this store to a single file.
|
||||||
|
*/
|
||||||
|
public ConfigStore<T> file(File file) {
|
||||||
|
this.file = file;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Legacy file to read, if the file of this store does not exist yet.
|
||||||
|
*/
|
||||||
|
public ConfigStore<T> legacyFile(File legacyFile, Function<String, ? extends VersionedConfig> reader) {
|
||||||
|
this.legacyFile = legacyFile;
|
||||||
|
this.legacyFileReader = reader;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads a stored config and converts it to the current version.
|
||||||
|
*
|
||||||
|
* @throws RuntimeException if the config could not be read or converted
|
||||||
|
*/
|
||||||
|
public T read(String stored) {
|
||||||
|
for (LegacyFormat legacyFormat : legacyFormats) {
|
||||||
|
if (legacyFormat.matches().test(stored)) {
|
||||||
|
return update(legacyFormat.reader().apply(stored));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
JsonObject json = JsonParser.parseString(stored).getAsJsonObject();
|
||||||
|
int storedVersion;
|
||||||
|
if (json.has("version")) {
|
||||||
|
storedVersion = json.get("version").getAsInt();
|
||||||
|
} else if (unversionedVersion != null) {
|
||||||
|
storedVersion = unversionedVersion;
|
||||||
|
json.addProperty("version", storedVersion);
|
||||||
|
} else {
|
||||||
|
throw new IllegalStateException("Missing version of " + type.getSimpleName());
|
||||||
|
}
|
||||||
|
Class<? extends VersionedConfig> storedType = jsonVersions.get(storedVersion);
|
||||||
|
if (storedType == null) {
|
||||||
|
throw new IllegalStateException("Unknown version " + storedVersion + " of " + type.getSimpleName());
|
||||||
|
}
|
||||||
|
return update(gson.fromJson(json, storedType));
|
||||||
|
}
|
||||||
|
|
||||||
|
public String write(T config) {
|
||||||
|
return gson.toJson(config);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts a config to the current version.
|
||||||
|
*/
|
||||||
|
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||||
|
public T update(VersionedConfig config) {
|
||||||
|
while (config.getVersion() < version) {
|
||||||
|
ConfigConverter converter = converters.get(config.getVersion());
|
||||||
|
if (converter == null) {
|
||||||
|
throw new IllegalStateException("No converter found for version " + config.getVersion() + " of " + type.getSimpleName());
|
||||||
|
}
|
||||||
|
VersionedConfig converted = converter.convert(config);
|
||||||
|
if (converted.getVersion() <= config.getVersion()) {
|
||||||
|
throw new IllegalStateException("Version did not increase after conversion of version " + config.getVersion() + " of " + type.getSimpleName());
|
||||||
|
}
|
||||||
|
config = converted;
|
||||||
|
}
|
||||||
|
if (!type.isInstance(config)) {
|
||||||
|
throw new IllegalStateException("Version " + config.getVersion() + " is not the current version of " + type.getSimpleName());
|
||||||
|
}
|
||||||
|
return type.cast(config);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the config stored in the file, or the default config if the file is missing or empty
|
||||||
|
* @throws RuntimeException if the config could not be read or converted
|
||||||
|
*/
|
||||||
|
public T load(File file) throws IOException {
|
||||||
|
if (file.length() == 0) {
|
||||||
|
return defaultConfig.get();
|
||||||
|
}
|
||||||
|
return read(Files.readString(file.toPath(), StandardCharsets.UTF_8));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void save(File file, T config) throws IOException {
|
||||||
|
Files.writeString(file.toPath(), write(config), StandardCharsets.UTF_8);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the config of a player, loads it from the {@link UserConfig} and converts it if necessary.
|
||||||
|
*/
|
||||||
|
public T get(Player player) {
|
||||||
|
requireUserConfig();
|
||||||
|
return playerConfigs.computeIfAbsent(player.getUniqueId(), this::loadPlayer);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Save the config of a player, this does not unload the config.
|
||||||
|
*/
|
||||||
|
public void save(Player player) {
|
||||||
|
requireUserConfig();
|
||||||
|
T config = playerConfigs.get(player.getUniqueId());
|
||||||
|
if (config != null) {
|
||||||
|
savePlayer(player.getUniqueId(), config);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Save and unload the config of a player.
|
||||||
|
*/
|
||||||
|
public void unload(Player player) {
|
||||||
|
if (userConfigKey == null) return;
|
||||||
|
T config = playerConfigs.remove(player.getUniqueId());
|
||||||
|
if (config != null) {
|
||||||
|
savePlayer(player.getUniqueId(), config);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private T loadPlayer(UUID uuid) {
|
||||||
|
String stored = UserConfig.getConfig(uuid, userConfigKey);
|
||||||
|
if (stored == null) {
|
||||||
|
return defaultConfig.get();
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return read(stored);
|
||||||
|
} catch (Exception e) {
|
||||||
|
Bukkit.getLogger().log(Level.SEVERE, "Could not load " + type.getSimpleName() + " of " + uuid, e);
|
||||||
|
return defaultConfig.get();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void savePlayer(UUID uuid, T config) {
|
||||||
|
if (removeIf.test(config)) {
|
||||||
|
UserConfig.removePlayerConfig(uuid, userConfigKey);
|
||||||
|
} else {
|
||||||
|
UserConfig.updatePlayerConfig(uuid, userConfigKey, write(config));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void requireUserConfig() {
|
||||||
|
if (userConfigKey == null) {
|
||||||
|
throw new IllegalStateException(type.getSimpleName() + " is not stored in the UserConfig");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the config of the file of this store, loads it and converts it if necessary.
|
||||||
|
*/
|
||||||
|
public T get() {
|
||||||
|
requireFile();
|
||||||
|
if (fileConfig == null) {
|
||||||
|
fileConfig = loadFile();
|
||||||
|
save();
|
||||||
|
}
|
||||||
|
return fileConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Save the config to the file of this store, if it was loaded.
|
||||||
|
*/
|
||||||
|
public void save() {
|
||||||
|
requireFile();
|
||||||
|
if (fileConfig == null) return;
|
||||||
|
try {
|
||||||
|
save(file, fileConfig);
|
||||||
|
} catch (IOException e) {
|
||||||
|
Bukkit.getLogger().log(Level.SEVERE, "Could not write " + file, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private T loadFile() {
|
||||||
|
try {
|
||||||
|
if (file.length() != 0) {
|
||||||
|
return load(file);
|
||||||
|
}
|
||||||
|
if (legacyFile != null && legacyFile.length() != 0) {
|
||||||
|
return update(legacyFileReader.apply(Files.readString(legacyFile.toPath(), StandardCharsets.UTF_8)));
|
||||||
|
}
|
||||||
|
return defaultConfig.get();
|
||||||
|
} catch (Exception e) {
|
||||||
|
Bukkit.getLogger().log(Level.SEVERE, "Could not load " + file + ", using default " + type.getSimpleName(), e);
|
||||||
|
backupBrokenFile();
|
||||||
|
return defaultConfig.get();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void backupBrokenFile() {
|
||||||
|
if (file.length() == 0) return;
|
||||||
|
try {
|
||||||
|
Files.copy(file.toPath(), new File(file.getParentFile(), file.getName() + ".broken").toPath(), StandardCopyOption.REPLACE_EXISTING);
|
||||||
|
} catch (IOException e) {
|
||||||
|
Bukkit.getLogger().log(Level.SEVERE, "Could not backup broken " + file, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void requireFile() {
|
||||||
|
if (file == null) {
|
||||||
|
throw new IllegalStateException(type.getSimpleName() + " is not bound to a file");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void unloadAll(Player player) {
|
||||||
|
STORES.forEach(store -> store.unload(player));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Saves every loaded config and unloads the configs of the players.
|
||||||
|
*/
|
||||||
|
public static void saveAll() {
|
||||||
|
for (ConfigStore<?> store : STORES) {
|
||||||
|
if (store.userConfigKey != null) {
|
||||||
|
store.playerConfigs.forEach((uuid, config) -> saveUnchecked(store, uuid, config));
|
||||||
|
store.playerConfigs.clear();
|
||||||
|
}
|
||||||
|
if (store.file != null) {
|
||||||
|
store.save();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
private static <T extends VersionedConfig> void saveUnchecked(ConfigStore<T> store, UUID uuid, Object config) {
|
||||||
|
store.savePlayer(uuid, (T) config);
|
||||||
|
}
|
||||||
|
}
|
||||||
+17
-9
@@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* This file is a part of the SteamWar software.
|
* This file is a part of the SteamWar software.
|
||||||
*
|
*
|
||||||
* Copyright (C) 2025 SteamWar.de-Serverteam
|
* Copyright (C) 2026 SteamWar.de-Serverteam
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU Affero General Public License as published by
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
@@ -17,21 +17,29 @@
|
|||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package de.steamwar.core.events;
|
package de.steamwar.bausystem.config;
|
||||||
|
|
||||||
import de.steamwar.core.Core;
|
import de.steamwar.core.CRIUSleepEvent;
|
||||||
import de.steamwar.linkage.Linked;
|
import de.steamwar.linkage.Linked;
|
||||||
import org.bukkit.event.EventHandler;
|
import org.bukkit.event.EventHandler;
|
||||||
import org.bukkit.event.EventPriority;
|
import org.bukkit.event.EventPriority;
|
||||||
import org.bukkit.event.Listener;
|
import org.bukkit.event.Listener;
|
||||||
import org.bukkit.event.player.AsyncPlayerChatEvent;
|
import org.bukkit.event.player.PlayerQuitEvent;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Saves and unloads the configs of the {@link ConfigStore}s bound to the UserConfig
|
||||||
|
* when a player quits, configs are loaded on first use.
|
||||||
|
*/
|
||||||
@Linked
|
@Linked
|
||||||
public class ChattingEvent implements Listener {
|
public class ConfigStoreListener implements Listener {
|
||||||
|
|
||||||
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
|
@EventHandler(priority = EventPriority.MONITOR)
|
||||||
private void onChat(AsyncPlayerChatEvent event) {
|
public void onPlayerQuit(PlayerQuitEvent event) {
|
||||||
event.setCancelled(true);
|
ConfigStore.unloadAll(event.getPlayer());
|
||||||
Core.MESSAGE.broadcastPrefixless("LOCAL_CHAT", event.getPlayer().getDisplayName(), event.getMessage());
|
}
|
||||||
|
|
||||||
|
@EventHandler
|
||||||
|
public void onCRIUSleep(CRIUSleepEvent event) {
|
||||||
|
ConfigStore.saveAll();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
/*
|
||||||
|
* This file is a part of the SteamWar software.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2026 SteamWar.de-Serverteam
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program 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 Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.steamwar.bausystem.config;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Common interface of every version of a config stored by a {@link ConfigStore},
|
||||||
|
* the current version (e.g. Config) and every older version (ConfigV1, ConfigV2, ...).
|
||||||
|
*/
|
||||||
|
public interface VersionedConfig {
|
||||||
|
|
||||||
|
int getVersion();
|
||||||
|
}
|
||||||
+95
@@ -0,0 +1,95 @@
|
|||||||
|
/*
|
||||||
|
* This file is a part of the SteamWar software.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2026 SteamWar.de-Serverteam
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program 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 Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.steamwar.bausystem.config.legacy;
|
||||||
|
|
||||||
|
import lombok.experimental.UtilityClass;
|
||||||
|
import org.bukkit.configuration.serialization.ConfigurationSerialization;
|
||||||
|
import org.bukkit.inventory.ItemStack;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deserializes {@link ItemStack}s that were serialized by YAPION, read with {@link YapionReader}.
|
||||||
|
*/
|
||||||
|
@UtilityClass
|
||||||
|
@Deprecated
|
||||||
|
public class YapionItemDeserializer {
|
||||||
|
|
||||||
|
private static final String TYPE_IDENTIFIER = "@type";
|
||||||
|
|
||||||
|
public ItemStack deserializeItem(Object yapion) {
|
||||||
|
if (yapion == null) return null;
|
||||||
|
Object result = deserialize(yapion);
|
||||||
|
if (result instanceof ItemStack itemStack) return itemStack;
|
||||||
|
throw new IllegalArgumentException("Not an ItemStack: " + result);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Object deserialize(Object yapion) {
|
||||||
|
if (yapion instanceof List<?> list) {
|
||||||
|
List<Object> result = new ArrayList<>();
|
||||||
|
list.forEach(o -> result.add(deserialize(o)));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
if (yapion instanceof YapionReader.YapionMap map) {
|
||||||
|
Map<Object, Object> result = new LinkedHashMap<>();
|
||||||
|
map.entries().forEach((key, value) -> result.put(deserialize(key), deserialize(value)));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
if (yapion instanceof Map<?, ?> object) {
|
||||||
|
return deserializeObject(object);
|
||||||
|
}
|
||||||
|
return yapion;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Object deserializeObject(Map<?, ?> object) {
|
||||||
|
Map<String, Object> data = new LinkedHashMap<>();
|
||||||
|
object.forEach((key, value) -> {
|
||||||
|
if (!TYPE_IDENTIFIER.equals(key)) data.put((String) key, deserialize(value));
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!(object.get(TYPE_IDENTIFIER) instanceof String type)) {
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
Class<?> javaType = javaUtilClass(type);
|
||||||
|
if (javaType != null && Collection.class.isAssignableFrom(javaType)) {
|
||||||
|
return data.getOrDefault("values", new ArrayList<>());
|
||||||
|
}
|
||||||
|
if (javaType != null && Map.class.isAssignableFrom(javaType)) {
|
||||||
|
return data.containsKey("values") ? data.get("values") : data;
|
||||||
|
}
|
||||||
|
|
||||||
|
data.put(ConfigurationSerialization.SERIALIZED_TYPE_KEY, type.equals("org.bukkit.inventory.meta.ItemMeta") ? "ItemMeta" : type);
|
||||||
|
return ConfigurationSerialization.deserializeObject(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Class<?> javaUtilClass(String type) {
|
||||||
|
if (!type.startsWith("java.util.")) return null;
|
||||||
|
try {
|
||||||
|
return Class.forName(type);
|
||||||
|
} catch (ClassNotFoundException e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,323 @@
|
|||||||
|
/*
|
||||||
|
* This file is a part of the SteamWar software.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2026 SteamWar.de-Serverteam
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program 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 Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.steamwar.bausystem.config.legacy;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Minimal reader for the legacy YAPION format, only used to migrate old data.
|
||||||
|
* <p>
|
||||||
|
* The result is built from plain java objects:
|
||||||
|
* <ul>
|
||||||
|
* <li>YAPION objects {@code {key(value)}} become a {@link LinkedHashMap} of String to value</li>
|
||||||
|
* <li>YAPION arrays {@code [a,b]} become a {@link List}</li>
|
||||||
|
* <li>YAPION maps {@code <(a):(b)>} become a {@link YapionMap}</li>
|
||||||
|
* <li>YAPION values {@code (value)} become null, Boolean, Number or String</li>
|
||||||
|
* </ul>
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
public class YapionReader {
|
||||||
|
|
||||||
|
private static final Pattern INTEGER = Pattern.compile("[-+]?\\d+");
|
||||||
|
private static final Pattern DECIMAL = Pattern.compile("[-+]?(\\d+\\.\\d*|\\.\\d+|\\d+)([eE][-+]?\\d+)?");
|
||||||
|
|
||||||
|
public record YapionMap(Map<Object, Object> entries) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Map<String, Object> parse(String yapion) {
|
||||||
|
YapionReader reader = new YapionReader(yapion);
|
||||||
|
reader.skipWhitespace();
|
||||||
|
Map<String, Object> result = reader.readObject();
|
||||||
|
reader.skipWhitespace();
|
||||||
|
if (reader.index != yapion.length()) {
|
||||||
|
throw reader.error("Unexpected trailing content");
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static double getDouble(Map<?, ?> object, String key, double defaultValue) {
|
||||||
|
return object.get(key) instanceof Number value ? value.doubleValue() : defaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int getInt(Map<?, ?> object, String key, int defaultValue) {
|
||||||
|
return object.get(key) instanceof Number value ? value.intValue() : defaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean getBoolean(Map<?, ?> object, String key, boolean defaultValue) {
|
||||||
|
return object.get(key) instanceof Boolean value ? value : defaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getString(Map<?, ?> object, String key, String defaultValue) {
|
||||||
|
return object.get(key) instanceof String value ? value : defaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the entries of the array that are YAPION objects
|
||||||
|
*/
|
||||||
|
public static List<Map<?, ?>> getObjects(Map<?, ?> object, String key) {
|
||||||
|
List<Map<?, ?>> objects = new ArrayList<>();
|
||||||
|
if (object.get(key) instanceof List<?> array) {
|
||||||
|
array.forEach(o -> {
|
||||||
|
if (o instanceof Map<?, ?> map) objects.add(map);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return objects;
|
||||||
|
}
|
||||||
|
|
||||||
|
private final String input;
|
||||||
|
private int index = 0;
|
||||||
|
|
||||||
|
private YapionReader(String input) {
|
||||||
|
this.input = input;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Object readAny() {
|
||||||
|
return switch (peek()) {
|
||||||
|
case '{' -> readObject();
|
||||||
|
case '[' -> readArray();
|
||||||
|
case '<' -> readMap();
|
||||||
|
case '(' -> readValue();
|
||||||
|
default -> throw error("Expected '{', '[', '<' or '('");
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private Map<String, Object> readObject() {
|
||||||
|
expect('{');
|
||||||
|
Map<String, Object> object = new LinkedHashMap<>();
|
||||||
|
while (true) {
|
||||||
|
skipWhitespace();
|
||||||
|
if (peek() == '}') {
|
||||||
|
index++;
|
||||||
|
return object;
|
||||||
|
}
|
||||||
|
StringBuilder key = new StringBuilder();
|
||||||
|
while (true) {
|
||||||
|
char c = peek();
|
||||||
|
if (c == '(' || c == '{' || c == '[' || c == '<') break;
|
||||||
|
if (c == '}') throw error("Key without value");
|
||||||
|
key.append(readChar());
|
||||||
|
}
|
||||||
|
object.put(key.toString(), readAny());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<Object> readArray() {
|
||||||
|
expect('[');
|
||||||
|
List<Object> array = new ArrayList<>();
|
||||||
|
skipWhitespace();
|
||||||
|
if (peek() == ']') {
|
||||||
|
index++;
|
||||||
|
return array;
|
||||||
|
}
|
||||||
|
while (true) {
|
||||||
|
int start = index;
|
||||||
|
skipWhitespace();
|
||||||
|
char c = peek();
|
||||||
|
if (c == '{' || c == '[' || c == '<' || c == '(') {
|
||||||
|
array.add(readAny());
|
||||||
|
skipWhitespace();
|
||||||
|
} else {
|
||||||
|
// Whitespace is part of plain values
|
||||||
|
index = start;
|
||||||
|
StringBuilder value = new StringBuilder();
|
||||||
|
while (peek() != ',' && peek() != ']') {
|
||||||
|
value.append(readChar());
|
||||||
|
}
|
||||||
|
array.add(typed(value.toString()));
|
||||||
|
}
|
||||||
|
char separator = input.charAt(index++);
|
||||||
|
if (separator == ']') return array;
|
||||||
|
if (separator != ',') throw error("Expected ',' or ']'");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private YapionMap readMap() {
|
||||||
|
expect('<');
|
||||||
|
Map<Object, Object> map = new LinkedHashMap<>();
|
||||||
|
while (true) {
|
||||||
|
skipWhitespace();
|
||||||
|
if (peek() == '>') {
|
||||||
|
index++;
|
||||||
|
return new YapionMap(map);
|
||||||
|
}
|
||||||
|
Object key = readAny();
|
||||||
|
skipWhitespace();
|
||||||
|
expect(':');
|
||||||
|
skipWhitespace();
|
||||||
|
map.put(key, readAny());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* YAPION does not escape parentheses in values, balanced parentheses are part of the value.
|
||||||
|
* A backslash only escapes a backslash, a unicode sequence or a control character, otherwise it is kept.
|
||||||
|
*/
|
||||||
|
private Object readValue() {
|
||||||
|
expect('(');
|
||||||
|
StringBuilder value = new StringBuilder();
|
||||||
|
int depth = 0;
|
||||||
|
while (true) {
|
||||||
|
char c = peek();
|
||||||
|
if (c == '\\') {
|
||||||
|
value.append(readValueEscape());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
index++;
|
||||||
|
if (c == '(') {
|
||||||
|
depth++;
|
||||||
|
} else if (c == ')') {
|
||||||
|
if (depth == 0) break;
|
||||||
|
depth--;
|
||||||
|
}
|
||||||
|
value.append(c);
|
||||||
|
}
|
||||||
|
return typed(value.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
private String readValueEscape() {
|
||||||
|
index++;
|
||||||
|
if (index >= input.length()) return "\\";
|
||||||
|
char escaped = input.charAt(index);
|
||||||
|
switch (escaped) {
|
||||||
|
case '\\', '(', ')':
|
||||||
|
index++;
|
||||||
|
return String.valueOf(escaped);
|
||||||
|
case 'n':
|
||||||
|
index++;
|
||||||
|
return "\n";
|
||||||
|
case 't':
|
||||||
|
index++;
|
||||||
|
return "\t";
|
||||||
|
case 'r':
|
||||||
|
index++;
|
||||||
|
return "\r";
|
||||||
|
case 'u':
|
||||||
|
if (index + 5 <= input.length() && input.substring(index + 1, index + 5).matches("[0-9a-fA-F]{4}")) {
|
||||||
|
char unicode = (char) Integer.parseInt(input.substring(index + 1, index + 5), 16);
|
||||||
|
index += 5;
|
||||||
|
return String.valueOf(unicode);
|
||||||
|
}
|
||||||
|
return "\\";
|
||||||
|
default:
|
||||||
|
return "\\";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads one character of a key or plain array value, resolving backslash escapes.
|
||||||
|
* A backslash that does not escape anything is kept, as the old player config
|
||||||
|
* collapsed escaped backslashes into a single one.
|
||||||
|
*/
|
||||||
|
private char readChar() {
|
||||||
|
char c = input.charAt(index++);
|
||||||
|
if (c != '\\') return c;
|
||||||
|
if (index >= input.length()) throw error("Unterminated escape");
|
||||||
|
char escaped = input.charAt(index);
|
||||||
|
switch (escaped) {
|
||||||
|
case 'n':
|
||||||
|
index++;
|
||||||
|
return '\n';
|
||||||
|
case 't':
|
||||||
|
index++;
|
||||||
|
return '\t';
|
||||||
|
case 'r':
|
||||||
|
index++;
|
||||||
|
return '\r';
|
||||||
|
case 'u':
|
||||||
|
if (index + 5 <= input.length() && input.substring(index + 1, index + 5).matches("[0-9a-fA-F]{4}")) {
|
||||||
|
char unicode = (char) Integer.parseInt(input.substring(index + 1, index + 5), 16);
|
||||||
|
index += 5;
|
||||||
|
return unicode;
|
||||||
|
}
|
||||||
|
return '\\';
|
||||||
|
case '\\', ',', ' ', '(', ')', '{', '}', '[', ']', '<', '>', '"', ':', '@':
|
||||||
|
index++;
|
||||||
|
return escaped;
|
||||||
|
default:
|
||||||
|
return '\\';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Object typed(String value) {
|
||||||
|
if (value.equals("null")) return null;
|
||||||
|
if (value.equals("true")) return true;
|
||||||
|
if (value.equals("false")) return false;
|
||||||
|
if (value.length() >= 2 && value.startsWith("\"") && value.endsWith("\"")) {
|
||||||
|
return value.substring(1, value.length() - 1);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
if (INTEGER.matcher(value).matches()) {
|
||||||
|
long l = Long.parseLong(value);
|
||||||
|
if (l >= Integer.MIN_VALUE && l <= Integer.MAX_VALUE) return (int) l;
|
||||||
|
return l;
|
||||||
|
}
|
||||||
|
if (value.length() > 1) {
|
||||||
|
String number = value.substring(0, value.length() - 1);
|
||||||
|
switch (value.charAt(value.length() - 1)) {
|
||||||
|
case 'L':
|
||||||
|
if (INTEGER.matcher(number).matches()) return Long.parseLong(number);
|
||||||
|
break;
|
||||||
|
case 'S':
|
||||||
|
if (INTEGER.matcher(number).matches()) return Short.parseShort(number);
|
||||||
|
break;
|
||||||
|
case 'B':
|
||||||
|
if (INTEGER.matcher(number).matches()) return Byte.parseByte(number);
|
||||||
|
break;
|
||||||
|
case 'F':
|
||||||
|
if (DECIMAL.matcher(number).matches()) return Float.parseFloat(number);
|
||||||
|
break;
|
||||||
|
case 'D':
|
||||||
|
if (DECIMAL.matcher(number).matches()) return Double.parseDouble(number);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (DECIMAL.matcher(value).matches()) return Double.parseDouble(value);
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
// Not a number, fall through to String
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void skipWhitespace() {
|
||||||
|
while (index < input.length() && Character.isWhitespace(input.charAt(index))) {
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private char peek() {
|
||||||
|
if (index >= input.length()) throw error("Unexpected end of input");
|
||||||
|
return input.charAt(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void expect(char c) {
|
||||||
|
if (peek() != c) throw error("Expected '" + c + "'");
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
|
||||||
|
private IllegalArgumentException error(String message) {
|
||||||
|
return new IllegalArgumentException(message + " at index " + index);
|
||||||
|
}
|
||||||
|
}
|
||||||
+63
@@ -0,0 +1,63 @@
|
|||||||
|
/*
|
||||||
|
* This file is a part of the SteamWar software.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2026 SteamWar.de-Serverteam
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program 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 Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.steamwar.bausystem.config.serializer;
|
||||||
|
|
||||||
|
import com.google.gson.TypeAdapter;
|
||||||
|
import com.google.gson.stream.JsonReader;
|
||||||
|
import com.google.gson.stream.JsonToken;
|
||||||
|
import com.google.gson.stream.JsonWriter;
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.inventory.ItemStack;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Base64;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stores an {@link ItemStack} as Base64 of {@link ItemStack#serializeAsBytes()}, which
|
||||||
|
* contains the data version and gets upgraded on newer Minecraft versions.
|
||||||
|
* Empty items are stored as null, items that can't be read are deserialized as null.
|
||||||
|
*/
|
||||||
|
public class ItemStackTypeAdapter extends TypeAdapter<ItemStack> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void write(JsonWriter out, ItemStack itemStack) throws IOException {
|
||||||
|
if (itemStack == null || itemStack.isEmpty()) {
|
||||||
|
out.nullValue();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
out.value(Base64.getEncoder().encodeToString(itemStack.serializeAsBytes()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ItemStack read(JsonReader in) throws IOException {
|
||||||
|
if (in.peek() == JsonToken.NULL) {
|
||||||
|
in.nextNull();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
String value = in.nextString();
|
||||||
|
try {
|
||||||
|
return ItemStack.deserializeBytes(Base64.getDecoder().decode(value));
|
||||||
|
} catch (Exception e) {
|
||||||
|
Bukkit.getLogger().log(Level.WARNING, "Could not deserialize ItemStack", e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* This file is a part of the SteamWar software.
|
* This file is a part of the SteamWar software.
|
||||||
*
|
*
|
||||||
* Copyright (C) 2025 SteamWar.de-Serverteam
|
* Copyright (C) 2026 SteamWar.de-Serverteam
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU Affero General Public License as published by
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
@@ -19,129 +19,77 @@
|
|||||||
|
|
||||||
package de.steamwar.bausystem.configplayer;
|
package de.steamwar.bausystem.configplayer;
|
||||||
|
|
||||||
import de.steamwar.bausystem.configplayer.serializer.ConfigurationSerializableSerializer;
|
import com.google.gson.GsonBuilder;
|
||||||
import de.steamwar.linkage.Linked;
|
import de.steamwar.bausystem.config.ConfigConverter;
|
||||||
import de.steamwar.sql.UserConfig;
|
import de.steamwar.bausystem.config.ConfigStore;
|
||||||
|
import de.steamwar.bausystem.config.VersionedConfig;
|
||||||
|
import de.steamwar.bausystem.config.serializer.ItemStackTypeAdapter;
|
||||||
|
import de.steamwar.bausystem.configplayer.converter.ConfigV1ToV2Converter;
|
||||||
|
import de.steamwar.bausystem.features.hotbar.DefaultHotbar;
|
||||||
|
import lombok.AccessLevel;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import org.bukkit.Bukkit;
|
import lombok.Setter;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.inventory.ItemStack;
|
||||||
import org.bukkit.event.EventHandler;
|
|
||||||
import org.bukkit.event.Listener;
|
|
||||||
import org.bukkit.event.player.PlayerJoinEvent;
|
|
||||||
import org.bukkit.event.player.PlayerQuitEvent;
|
|
||||||
import yapion.hierarchy.output.StringOutput;
|
|
||||||
import yapion.hierarchy.types.YAPIONObject;
|
|
||||||
import yapion.parser.YAPIONParser;
|
|
||||||
import yapion.serializing.SerializeManager;
|
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.UUID;
|
|
||||||
import java.util.logging.Level;
|
|
||||||
|
|
||||||
@Linked
|
/**
|
||||||
public class Config implements Listener {
|
* The current player config. It is stored as JSON, fields missing in the
|
||||||
|
* stored JSON keep the default value of their initializer, so adding a new
|
||||||
|
* field does not need a new version.
|
||||||
|
* <p>
|
||||||
|
* When a field gets removed, renamed or its meaning changes, copy this class
|
||||||
|
* to ConfigV{@link #VERSION} (without the {@link #STORE}), increase {@link #VERSION},
|
||||||
|
* write a {@link ConfigConverter} from the copy to this class and register it in the {@link #STORE}.
|
||||||
|
*/
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
public class Config implements VersionedConfig {
|
||||||
|
|
||||||
static {
|
public static final int VERSION = 2;
|
||||||
SerializeManager.add(new ConfigurationSerializableSerializer());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Getter
|
public static final ConfigStore<Config> STORE = new ConfigStore<>(Config.class, VERSION, Config::new)
|
||||||
private static Config instance;
|
.gson(new GsonBuilder().registerTypeHierarchyAdapter(ItemStack.class, new ItemStackTypeAdapter()).create())
|
||||||
|
.legacy(s -> s.startsWith("{@version(1)"), ConfigV1::parse)
|
||||||
|
.converter(new ConfigV1ToV2Converter())
|
||||||
|
.userConfig("bausystem");
|
||||||
|
|
||||||
{
|
@Setter(AccessLevel.NONE)
|
||||||
instance = this;
|
protected int version = VERSION;
|
||||||
}
|
|
||||||
|
|
||||||
private final Map<UUID, YAPIONObject> playerConfigurations = new HashMap<>();
|
|
||||||
|
|
||||||
private static final Map<Integer, ConfigConverter> CONFIG_CONVERTER_MAP = new HashMap<>();
|
|
||||||
|
|
||||||
public static void addConfigConverter(ConfigConverter configConverter) {
|
|
||||||
CONFIG_CONVERTER_MAP.putIfAbsent(configConverter.version(), configConverter);
|
|
||||||
}
|
|
||||||
|
|
||||||
@EventHandler
|
|
||||||
public void onPlayerJoin(PlayerJoinEvent event) {
|
|
||||||
get(event.getPlayer());
|
|
||||||
}
|
|
||||||
|
|
||||||
@EventHandler
|
|
||||||
public void onPlayerQuit(PlayerQuitEvent event) {
|
|
||||||
save(event.getPlayer());
|
|
||||||
playerConfigurations.remove(event.getPlayer().getUniqueId());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a PlayerConfig, optionally loads it from the DataBase and migrates it if necessary.
|
* BauGui item id to inventory slot
|
||||||
*
|
|
||||||
* @param player the player from whom to get the config.
|
|
||||||
* @return the config object
|
|
||||||
*/
|
*/
|
||||||
public YAPIONObject get(Player player) {
|
protected Map<Integer, Integer> bauGui = defaultBauGui();
|
||||||
UUID uuid = player.getUniqueId();
|
protected int bauGuiSize = 45;
|
||||||
if (!playerConfigurations.containsKey(uuid)) {
|
|
||||||
String s = UserConfig.getConfig(uuid, "bausystem");
|
|
||||||
YAPIONObject yapionObject;
|
|
||||||
if (s == null) {
|
|
||||||
yapionObject = ConfigCreator.createDefaultConfig();
|
|
||||||
} else {
|
|
||||||
yapionObject = YAPIONParser.parse(s);
|
|
||||||
}
|
|
||||||
yapionObject = update(yapionObject);
|
|
||||||
playerConfigurations.put(uuid, yapionObject);
|
|
||||||
return yapionObject;
|
|
||||||
}
|
|
||||||
return playerConfigurations.get(uuid);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void saveAll() {
|
|
||||||
playerConfigurations.forEach((uuid, yapionObject) -> {
|
|
||||||
String string = yapionObject.toYAPION(new StringOutput()).getResult().replaceAll("\\+", "\\");
|
|
||||||
UserConfig.updatePlayerConfig(uuid, "bausystem", string);
|
|
||||||
});
|
|
||||||
playerConfigurations.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Save a PlayerConfig, this does not remove the key value mapping from the map.
|
* Slots 0-8 are the hotbar, slots 9-12 the armor contents
|
||||||
*
|
|
||||||
* @param player the player to save the config.
|
|
||||||
*/
|
*/
|
||||||
public void save(Player player) {
|
protected ItemStack[] hotbar = DefaultHotbar.defaultHotbar();
|
||||||
UUID uuid = player.getUniqueId();
|
|
||||||
if (playerConfigurations.containsKey(uuid)) {
|
|
||||||
YAPIONObject yapionObject = playerConfigurations.get(uuid);
|
|
||||||
String string = yapionObject.toYAPION(new StringOutput()).getResult().replaceAll("\\\\+", "\\\\");
|
|
||||||
UserConfig.updatePlayerConfig(uuid, "bausystem", string);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private YAPIONObject update(YAPIONObject yapionObject) {
|
protected boolean nightVision = false;
|
||||||
int version = yapionObject.getPlainValue("@version");
|
protected boolean tntClickDetails = true;
|
||||||
while (version < ConfigCreator.currentVersion) {
|
protected boolean detonatorAutostart = false;
|
||||||
ConfigConverter configConverter = CONFIG_CONVERTER_MAP.getOrDefault(version, null);
|
protected boolean inventoryFill = false;
|
||||||
if (configConverter == null) {
|
protected boolean rayVisualizer = false;
|
||||||
Bukkit.getLogger().log(Level.SEVERE, "No updater found for version " + version);
|
protected boolean smartPlace = false;
|
||||||
return ConfigCreator.createDefaultConfig();
|
protected boolean blockCounter = false;
|
||||||
}
|
protected boolean depthMessage = true;
|
||||||
try {
|
|
||||||
configConverter.update(yapionObject);
|
public static Map<Integer, Integer> defaultBauGui() {
|
||||||
} catch (Exception e) {
|
// 0: ? | 1: ? | 2: 10 | 3: 3 | 4: 7 | 5: 17 | 6: 15 | 7: ? | 8: ?
|
||||||
Bukkit.getLogger().log(Level.SEVERE, e.getMessage(), e);
|
// 9: 5 | 10: ? | 11: ? | 12: ? | 13: ? | 14: ? | 15: ? | 16: ? | 17: 16
|
||||||
return ConfigCreator.createDefaultConfig();
|
// 18: 4 | 19: ? | 20: 19 | 21: 21 | 22: 9 | 23: 0 | 24: 1 | 25: ? | 26: 11
|
||||||
}
|
// 27: 6 | 28: ? | 29: ? | 30: ? | 31: ? | 32: ? | 33: ? | 34: ? | 35: 18
|
||||||
int newVersion = yapionObject.getPlainValue("@version");
|
// 36: ? | 37: 23 | 38: 20 | 39: 8 | 40: 22 | 41: 26 | 42: 12 | 43: 14 | 44: ?
|
||||||
if (version == newVersion) {
|
Map<Integer, Integer> bauGui = new HashMap<>();
|
||||||
Bukkit.getLogger().log(Level.SEVERE, "Version Tag was the same after conversion");
|
bauGui.put(10, 2); bauGui.put(3, 3); bauGui.put(7, 4); bauGui.put(17, 5); bauGui.put(15, 6);
|
||||||
return ConfigCreator.createDefaultConfig();
|
bauGui.put(5, 9); bauGui.put(4, 18); bauGui.put(6, 27);
|
||||||
}
|
bauGui.put(16, 17); bauGui.put(11, 26); bauGui.put(18, 35);
|
||||||
if (newVersion < version) {
|
bauGui.put(19, 20); bauGui.put(21, 21); bauGui.put(9, 22); bauGui.put(0, 23); bauGui.put(1, 24);
|
||||||
Bukkit.getLogger().log(Level.SEVERE, "Version Tag was earlier after conversion");
|
bauGui.put(23, 37); bauGui.put(20, 38); bauGui.put(8, 39); bauGui.put(22, 40); bauGui.put(26, 41); bauGui.put(12, 42); bauGui.put(14, 43);
|
||||||
return ConfigCreator.createDefaultConfig();
|
return bauGui;
|
||||||
}
|
|
||||||
version = newVersion;
|
|
||||||
}
|
|
||||||
return yapionObject;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,56 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is a part of the SteamWar software.
|
|
||||||
*
|
|
||||||
* Copyright (C) 2025 SteamWar.de-Serverteam
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU Affero General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program 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 Affero General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package de.steamwar.bausystem.configplayer;
|
|
||||||
|
|
||||||
import yapion.hierarchy.types.YAPIONObject;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A new {@link ConfigConverter} should be written when you remove anything
|
|
||||||
* from the Config or modify any mayor part. When you move anything from
|
|
||||||
* any key to any other key you should write a new {@link ConfigConverter}.
|
|
||||||
* For adding any new key you should be able to get the default without
|
|
||||||
* having it in the Config. Anything you need to change you should also
|
|
||||||
* change the {@link ConfigCreator} accordingly, to produce the new Config.
|
|
||||||
*/
|
|
||||||
public interface ConfigConverter {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This describes the version this Converter can convert from. The version
|
|
||||||
* it should convert to is the version 1 above this number. But this is not
|
|
||||||
* a necessity. In the config Object as parameter given in {@link #update(YAPIONObject)}
|
|
||||||
* you should update the <b>@version</b> variable in the root object to the
|
|
||||||
* new version this converter produced.
|
|
||||||
*
|
|
||||||
* @return the version number
|
|
||||||
*/
|
|
||||||
int version();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This method should update everything needed to go from a lower config
|
|
||||||
* version to a higher. It should update the <b>@version</b> variable
|
|
||||||
* accordingly. Anything else is up the implementation. If anything goes wrong
|
|
||||||
* do not silently exit this method, throw an Exception. The updater Code will
|
|
||||||
* deal with it. Never leave the inputted object in a corrupted state.
|
|
||||||
*
|
|
||||||
* @param config the config object to update
|
|
||||||
*/
|
|
||||||
void update(YAPIONObject config);
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,61 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is a part of the SteamWar software.
|
|
||||||
*
|
|
||||||
* Copyright (C) 2025 SteamWar.de-Serverteam
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU Affero General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program 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 Affero General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package de.steamwar.bausystem.configplayer;
|
|
||||||
|
|
||||||
import de.steamwar.bausystem.features.hotbar.DefaultHotbar;
|
|
||||||
import lombok.experimental.UtilityClass;
|
|
||||||
import yapion.hierarchy.types.YAPIONObject;
|
|
||||||
|
|
||||||
@UtilityClass
|
|
||||||
public class ConfigCreator {
|
|
||||||
|
|
||||||
public static final int currentVersion = 1;
|
|
||||||
|
|
||||||
public YAPIONObject createDefaultConfig() {
|
|
||||||
YAPIONObject yapionObject = new YAPIONObject();
|
|
||||||
// This call should never be touched
|
|
||||||
yapionObject.add("@version", currentVersion);
|
|
||||||
|
|
||||||
// Any initialising goes into here
|
|
||||||
yapionObject.add("baugui", defaultBauGui());
|
|
||||||
|
|
||||||
// Default Hotbar Gui
|
|
||||||
yapionObject.add("hotbar", DefaultHotbar.defaultHotbar());
|
|
||||||
return yapionObject;
|
|
||||||
}
|
|
||||||
|
|
||||||
private YAPIONObject defaultBauGui() {
|
|
||||||
YAPIONObject yapionObject = new YAPIONObject();
|
|
||||||
|
|
||||||
// 0: ? | 1: ? | 2: 10 | 3: 3 | 4: 7 | 5: 17 | 6: 15 | 7: ? | 8: ?
|
|
||||||
// 9: 5 | 10: ? | 11: ? | 12: ? | 13: ? | 14: ? | 15: ? | 16: ? | 17: 16
|
|
||||||
// 18: 4 | 19: ? | 20: 19 | 21: 21 | 22: 9 | 23: 0 | 24: 1 | 25: ? | 26: 11
|
|
||||||
// 27: 6 | 28: ? | 29: ? | 30: ? | 31: ? | 32: ? | 33: ? | 34: ? | 35: 18
|
|
||||||
// 36: ? | 37: 23 | 38: 20 | 39: 8 | 40: 22 | 41: 26 | 42: 12 | 43: 14 | 44: ?
|
|
||||||
|
|
||||||
yapionObject.add("10", 2).add("3", 3).add("7", 4).add("17", 5).add("15", 6);
|
|
||||||
yapionObject.add("5", 9).add("4", 18).add("6", 27);
|
|
||||||
yapionObject.add("16", 17).add("11", 26).add("18", 35);
|
|
||||||
yapionObject.add("19", 20).add("21", 21).add("9", 22).add("0", 23).add("1", 24);
|
|
||||||
yapionObject.add("23", 37).add("20", 38).add("8", 39).add("22", 40).add("26", 41).add("12", 42).add("14", 43);
|
|
||||||
yapionObject.add("size", 45);
|
|
||||||
return yapionObject;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,104 @@
|
|||||||
|
/*
|
||||||
|
* This file is a part of the SteamWar software.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2026 SteamWar.de-Serverteam
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program 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 Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.steamwar.bausystem.configplayer;
|
||||||
|
|
||||||
|
import de.steamwar.bausystem.config.VersionedConfig;
|
||||||
|
import de.steamwar.bausystem.config.legacy.YapionReader;
|
||||||
|
import lombok.AccessLevel;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The legacy config that was stored in the YAPION format.
|
||||||
|
*/
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
public class ConfigV1 implements VersionedConfig {
|
||||||
|
|
||||||
|
public static final int VERSION = 1;
|
||||||
|
|
||||||
|
@Setter(AccessLevel.NONE)
|
||||||
|
protected int version = VERSION;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* BauGui item id to inventory slot
|
||||||
|
*/
|
||||||
|
protected Map<Integer, Integer> bauGui = new HashMap<>();
|
||||||
|
protected int bauGuiSize = 45;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Minecraft version to the hotbar serialized by YAPION, read with {@link YapionReader}.
|
||||||
|
* Use {@link de.steamwar.bausystem.config.legacy.YapionItemDeserializer} to get the items.
|
||||||
|
*/
|
||||||
|
protected Map<Integer, List<?>> hotbars = new HashMap<>();
|
||||||
|
|
||||||
|
protected boolean nightVision = false;
|
||||||
|
protected boolean tntClickDetails = true;
|
||||||
|
protected boolean detonatorAutostart = false;
|
||||||
|
protected boolean inventoryFill = false;
|
||||||
|
protected boolean rayVisualizer = false;
|
||||||
|
protected boolean smartPlace = false;
|
||||||
|
protected boolean blockCounter = false;
|
||||||
|
protected boolean depthMessage = true;
|
||||||
|
|
||||||
|
public static ConfigV1 parse(String yapion) {
|
||||||
|
Map<String, Object> object = YapionReader.parse(yapion);
|
||||||
|
ConfigV1 config = new ConfigV1();
|
||||||
|
config.version = (Integer) object.get("@version");
|
||||||
|
|
||||||
|
if (object.get("baugui") instanceof Map<?, ?> bauGui) {
|
||||||
|
bauGui.forEach((key, value) -> {
|
||||||
|
if (key.equals("size")) {
|
||||||
|
config.bauGuiSize = (Integer) value;
|
||||||
|
} else {
|
||||||
|
config.bauGui.put(Integer.parseInt((String) key), (Integer) value);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
object.forEach((key, value) -> {
|
||||||
|
if (!(value instanceof List<?> hotbar)) return;
|
||||||
|
if (key.equals("hotbar")) {
|
||||||
|
// Hotbars were stored without version prior to 1.19
|
||||||
|
config.hotbars.putIfAbsent(19, hotbar);
|
||||||
|
} else if (key.startsWith("hotbar-")) {
|
||||||
|
config.hotbars.put(Integer.parseInt(key.substring("hotbar-".length())), hotbar);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
config.nightVision = getBoolean(object, "nightvision", config.nightVision);
|
||||||
|
config.tntClickDetails = getBoolean(object, "tnt_click_details", config.tntClickDetails);
|
||||||
|
config.detonatorAutostart = getBoolean(object, "detonator-autostart", config.detonatorAutostart);
|
||||||
|
config.inventoryFill = getBoolean(object, "inventoryfill", config.inventoryFill);
|
||||||
|
config.rayVisualizer = getBoolean(object, "rayvisualizer", config.rayVisualizer);
|
||||||
|
config.smartPlace = getBoolean(object, "smartPlace", config.smartPlace);
|
||||||
|
config.blockCounter = getBoolean(object, "blockCounter", config.blockCounter);
|
||||||
|
config.depthMessage = getBoolean(object, "depth_message", config.depthMessage);
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean getBoolean(Map<String, Object> object, String key, boolean defaultValue) {
|
||||||
|
return object.get(key) instanceof Boolean value ? value : defaultValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
+75
@@ -0,0 +1,75 @@
|
|||||||
|
/*
|
||||||
|
* This file is a part of the SteamWar software.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2026 SteamWar.de-Serverteam
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program 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 Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.steamwar.bausystem.configplayer.converter;
|
||||||
|
|
||||||
|
import de.steamwar.bausystem.config.ConfigConverter;
|
||||||
|
import de.steamwar.bausystem.config.legacy.YapionItemDeserializer;
|
||||||
|
import de.steamwar.bausystem.configplayer.Config;
|
||||||
|
import de.steamwar.bausystem.configplayer.ConfigV1;
|
||||||
|
import de.steamwar.core.Core;
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.inventory.ItemStack;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
|
||||||
|
@Deprecated
|
||||||
|
public class ConfigV1ToV2Converter implements ConfigConverter<ConfigV1, Config> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int version() {
|
||||||
|
return ConfigV1.VERSION;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Config convert(ConfigV1 old) {
|
||||||
|
Config config = new Config();
|
||||||
|
config.setBauGui(new HashMap<>(old.getBauGui()));
|
||||||
|
config.setBauGuiSize(old.getBauGuiSize());
|
||||||
|
|
||||||
|
List<?> hotbar = old.getHotbars().get(Core.getVersion());
|
||||||
|
if (hotbar != null) {
|
||||||
|
config.setHotbar(convertHotbar(hotbar));
|
||||||
|
}
|
||||||
|
|
||||||
|
config.setNightVision(old.isNightVision());
|
||||||
|
config.setTntClickDetails(old.isTntClickDetails());
|
||||||
|
config.setDetonatorAutostart(old.isDetonatorAutostart());
|
||||||
|
config.setInventoryFill(old.isInventoryFill());
|
||||||
|
config.setRayVisualizer(old.isRayVisualizer());
|
||||||
|
config.setSmartPlace(old.isSmartPlace());
|
||||||
|
config.setBlockCounter(old.isBlockCounter());
|
||||||
|
config.setDepthMessage(old.isDepthMessage());
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ItemStack[] convertHotbar(List<?> hotbar) {
|
||||||
|
ItemStack[] items = new ItemStack[13];
|
||||||
|
for (int i = 0; i < Math.min(hotbar.size(), items.length); i++) {
|
||||||
|
try {
|
||||||
|
items[i] = YapionItemDeserializer.deserializeItem(hotbar.get(i));
|
||||||
|
} catch (Exception e) {
|
||||||
|
Bukkit.getLogger().log(Level.WARNING, "Could not convert hotbar item " + i, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return items;
|
||||||
|
}
|
||||||
|
}
|
||||||
-90
@@ -1,90 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is a part of the SteamWar software.
|
|
||||||
*
|
|
||||||
* Copyright (C) 2025 SteamWar.de-Serverteam
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU Affero General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program 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 Affero General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package de.steamwar.bausystem.configplayer.serializer;
|
|
||||||
|
|
||||||
import org.bukkit.configuration.serialization.ConfigurationSerializable;
|
|
||||||
import org.bukkit.configuration.serialization.ConfigurationSerialization;
|
|
||||||
import org.bukkit.inventory.ItemStack;
|
|
||||||
import org.bukkit.inventory.meta.ItemMeta;
|
|
||||||
import yapion.hierarchy.api.groups.YAPIONAnyType;
|
|
||||||
import yapion.hierarchy.types.YAPIONObject;
|
|
||||||
import yapion.serializing.api.SerializerObject;
|
|
||||||
import yapion.serializing.data.DeserializeData;
|
|
||||||
import yapion.serializing.data.SerializeData;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import static yapion.utils.IdentifierUtils.TYPE_IDENTIFIER;
|
|
||||||
|
|
||||||
public class ConfigurationSerializableSerializer extends SerializerObject<ConfigurationSerializable> {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Class<ConfigurationSerializable> type() {
|
|
||||||
return ConfigurationSerializable.class;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isInterface() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public YAPIONObject serialize(SerializeData<ConfigurationSerializable> serializeData) {
|
|
||||||
YAPIONObject yapionObject = new YAPIONObject();
|
|
||||||
yapionObject.add(TYPE_IDENTIFIER, serializeData.object.getClass().getTypeName());
|
|
||||||
if (serializeData.object instanceof ItemStack) {
|
|
||||||
yapionObject.add(TYPE_IDENTIFIER, ItemStack.class.getTypeName());
|
|
||||||
}
|
|
||||||
if (serializeData.object instanceof ItemMeta) {
|
|
||||||
yapionObject.add(TYPE_IDENTIFIER, ItemMeta.class.getTypeName());
|
|
||||||
}
|
|
||||||
Map<String, Object> serializeDataMap = serializeData.object.serialize();
|
|
||||||
serializeDataMap.forEach((s, o) -> {
|
|
||||||
YAPIONAnyType yapionAnyType = serializeData.serialize(o);
|
|
||||||
if (yapionAnyType instanceof YAPIONObject) {
|
|
||||||
YAPIONObject object = (YAPIONObject) yapionAnyType;
|
|
||||||
if (object.containsKey(TYPE_IDENTIFIER) && object.getPlainValue(TYPE_IDENTIFIER).equals("com.google.common.collect.RegularImmutableList")) {
|
|
||||||
object.put(TYPE_IDENTIFIER, ArrayList.class.getTypeName());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
yapionObject.add(s, yapionAnyType);
|
|
||||||
});
|
|
||||||
return yapionObject;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ConfigurationSerializable deserialize(DeserializeData<YAPIONObject> deserializeData) {
|
|
||||||
Map<String, Object> deserializeDataMap = new HashMap<>();
|
|
||||||
deserializeData.object.forEach((s, yapionAnyType) -> {
|
|
||||||
if (s.equals(TYPE_IDENTIFIER)) {
|
|
||||||
if (yapionAnyType.toString().equals("(org.bukkit.inventory.meta.ItemMeta)")) {
|
|
||||||
deserializeDataMap.put("==", "ItemMeta");
|
|
||||||
} else {
|
|
||||||
deserializeDataMap.put("==", deserializeData.deserialize(yapionAnyType));
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
deserializeDataMap.put(s, deserializeData.deserialize(yapionAnyType));
|
|
||||||
});
|
|
||||||
return ConfigurationSerialization.deserializeObject(deserializeDataMap);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -64,7 +64,7 @@ public class Depth {
|
|||||||
dimensions.setZ(Math.abs(dimensions.getZ()));
|
dimensions.setZ(Math.abs(dimensions.getZ()));
|
||||||
|
|
||||||
RegionUtils.message(region, player -> {
|
RegionUtils.message(region, player -> {
|
||||||
if (Config.getInstance().get(player).getPlainValueOrDefault("depth_message", true)) {
|
if (Config.STORE.get(player).isDepthMessage()) {
|
||||||
player.spigot().sendMessage(getMessage(player, dimensions.getBlockX() + 1, dimensions.getBlockY() + 1, dimensions.getBlockZ() + 1, tntCount));
|
player.spigot().sendMessage(getMessage(player, dimensions.getBlockX() + 1, dimensions.getBlockY() + 1, dimensions.getBlockZ() + 1, tntCount));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
+3
-3
@@ -34,11 +34,11 @@ public class DepthCommand extends SWCommand {
|
|||||||
|
|
||||||
@Register
|
@Register
|
||||||
public void toggle(Player player) {
|
public void toggle(Player player) {
|
||||||
if (Config.getInstance().get(player).getPlainValueOrDefault("depth_message", true)) {
|
if (Config.STORE.get(player).isDepthMessage()) {
|
||||||
Config.getInstance().get(player).put("depth_message", false);
|
Config.STORE.get(player).setDepthMessage(false);
|
||||||
BauSystem.MESSAGE.send("DEPTH_COUNTER_DISABLE", player);
|
BauSystem.MESSAGE.send("DEPTH_COUNTER_DISABLE", player);
|
||||||
} else {
|
} else {
|
||||||
Config.getInstance().get(player).put("depth_message", true);
|
Config.STORE.get(player).setDepthMessage(true);
|
||||||
BauSystem.MESSAGE.send("DEPTH_COUNTER_ENABLE", player);
|
BauSystem.MESSAGE.send("DEPTH_COUNTER_ENABLE", player);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -95,7 +95,7 @@ public class Detonator {
|
|||||||
|
|
||||||
public static void activateDetonator(DetonatorStorage detonator) {
|
public static void activateDetonator(DetonatorStorage detonator) {
|
||||||
Player p = detonator.getPlayer();
|
Player p = detonator.getPlayer();
|
||||||
if (Config.getInstance().get(p).getPlainValueOrDefault("detonator-autostart", false)) {
|
if (Config.STORE.get(p).isDetonatorAutostart()) {
|
||||||
AutostartListener.instance.activate(p);
|
AutostartListener.instance.activate(p);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -76,8 +76,8 @@ public class DetonatorCommand extends SWCommand {
|
|||||||
|
|
||||||
@Register(value = "autostart", description = "DETONATOR_HELP_AUTOSTART")
|
@Register(value = "autostart", description = "DETONATOR_HELP_AUTOSTART")
|
||||||
public void toggleAutostartTimer(Player p) {
|
public void toggleAutostartTimer(Player p) {
|
||||||
boolean current = Config.getInstance().get(p).getPlainValueOrDefault("detonator-autostart", false);
|
boolean current = Config.STORE.get(p).isDetonatorAutostart();
|
||||||
Config.getInstance().get(p).put("detonator-autostart", !current);
|
Config.STORE.get(p).setDetonatorAutostart(!current);
|
||||||
if (!current) {
|
if (!current) {
|
||||||
BauSystem.MESSAGE.send("DETONATOR_AUTOSTART_ENABLE", p);
|
BauSystem.MESSAGE.send("DETONATOR_AUTOSTART_ENABLE", p);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
+15
-16
@@ -28,7 +28,6 @@ import lombok.Setter;
|
|||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
import org.bukkit.scheduler.BukkitTask;
|
import org.bukkit.scheduler.BukkitTask;
|
||||||
import yapion.hierarchy.types.YAPIONObject;
|
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
@@ -52,15 +51,15 @@ public class BauGuiMapping {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
private final YAPIONObject object;
|
private final Config config;
|
||||||
@Getter
|
@Getter
|
||||||
private final Player owner;
|
private final Player owner;
|
||||||
@Getter
|
@Getter
|
||||||
@Setter
|
@Setter
|
||||||
private boolean saved;
|
private boolean saved;
|
||||||
|
|
||||||
protected BauGuiMapping(YAPIONObject object, Player p) {
|
protected BauGuiMapping(Config config, Player p) {
|
||||||
this.object = object;
|
this.config = config;
|
||||||
this.owner = p;
|
this.owner = p;
|
||||||
fromUUID.put(p.getUniqueId(), this);
|
fromUUID.put(p.getUniqueId(), this);
|
||||||
mappings.add(this);
|
mappings.add(this);
|
||||||
@@ -70,19 +69,18 @@ public class BauGuiMapping {
|
|||||||
BauGuiMapping mapping = fromUUID.get(p.getUniqueId());
|
BauGuiMapping mapping = fromUUID.get(p.getUniqueId());
|
||||||
|
|
||||||
if (mapping == null) {
|
if (mapping == null) {
|
||||||
YAPIONObject yapionObject = Config.getInstance().get(p);
|
mapping = new BauGuiMapping(Config.STORE.get(p), p);
|
||||||
mapping = new BauGuiMapping(yapionObject.getObject("baugui"), p);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return mapping;
|
return mapping;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isShown(int id) {
|
public boolean isShown(int id) {
|
||||||
return object.getPlainValueOrDefault(Integer.toString(id), -1) >= 0;
|
return config.getBauGui().getOrDefault(id, -1) >= 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getSlot(int id) {
|
public int getSlot(int id) {
|
||||||
return object.getPlainValue(Integer.toString(id));
|
return config.getBauGui().get(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Map<Integer, Integer> getMapping() {
|
public Map<Integer, Integer> getMapping() {
|
||||||
@@ -94,7 +92,7 @@ public class BauGuiMapping {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public int getSize() {
|
public int getSize() {
|
||||||
return object.getPlainValueOrDefault("size", 45);
|
return config.getBauGuiSize();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setSize(int size) {
|
public void setSize(int size) {
|
||||||
@@ -102,7 +100,7 @@ public class BauGuiMapping {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
object.add("size", size);
|
config.setBauGuiSize(size);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void tick() {
|
private void tick() {
|
||||||
@@ -111,16 +109,17 @@ public class BauGuiMapping {
|
|||||||
|
|
||||||
public void save() {
|
public void save() {
|
||||||
if (saved) return;
|
if (saved) return;
|
||||||
YAPIONObject config = Config.getInstance().get(owner);
|
Config current = Config.STORE.get(owner);
|
||||||
config.add("baugui", object);
|
current.setBauGui(config.getBauGui());
|
||||||
Config.getInstance().save(owner);
|
current.setBauGuiSize(config.getBauGuiSize());
|
||||||
|
Config.STORE.save(owner);
|
||||||
saved = true;
|
saved = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Map<Integer, Integer> internalReadMap() {
|
private Map<Integer, Integer> internalReadMap() {
|
||||||
Map<Integer, Integer> map = new HashMap<>();
|
Map<Integer, Integer> map = new HashMap<>();
|
||||||
for (Map.Entry<Integer, BauGuiItem> e : BauGUI.getITEMS().entrySet()) {
|
for (Map.Entry<Integer, BauGuiItem> e : BauGUI.getITEMS().entrySet()) {
|
||||||
Integer value = object.getPlainValueOrDefault(e.getKey().toString(), -1);
|
Integer value = config.getBauGui().getOrDefault(e.getKey(), -1);
|
||||||
if (value == -1) continue;
|
if (value == -1) continue;
|
||||||
map.put(e.getKey(), value);
|
map.put(e.getKey(), value);
|
||||||
}
|
}
|
||||||
@@ -129,10 +128,10 @@ public class BauGuiMapping {
|
|||||||
|
|
||||||
private void internalWriteMap(Map<Integer, Integer> map) {
|
private void internalWriteMap(Map<Integer, Integer> map) {
|
||||||
for (Map.Entry<Integer, BauGuiItem> e : BauGUI.getITEMS().entrySet()) {
|
for (Map.Entry<Integer, BauGuiItem> e : BauGUI.getITEMS().entrySet()) {
|
||||||
object.remove(e.getKey().toString());
|
config.getBauGui().remove(e.getKey());
|
||||||
Integer value = map.getOrDefault(e.getKey(), -1);
|
Integer value = map.getOrDefault(e.getKey(), -1);
|
||||||
if (value == -1) continue;
|
if (value == -1) continue;
|
||||||
object.add(e.getKey().toString(), value);
|
config.getBauGui().put(e.getKey(), value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+30
-40
@@ -20,39 +20,23 @@
|
|||||||
package de.steamwar.bausystem.features.hotbar;
|
package de.steamwar.bausystem.features.hotbar;
|
||||||
|
|
||||||
import de.steamwar.bausystem.configplayer.Config;
|
import de.steamwar.bausystem.configplayer.Config;
|
||||||
import de.steamwar.core.Core;
|
|
||||||
import lombok.experimental.UtilityClass;
|
import lombok.experimental.UtilityClass;
|
||||||
|
import org.bukkit.Material;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
import org.bukkit.inventory.ItemStack;
|
import org.bukkit.inventory.ItemStack;
|
||||||
import yapion.hierarchy.types.YAPIONArray;
|
import org.bukkit.inventory.meta.ItemMeta;
|
||||||
import yapion.hierarchy.types.YAPIONObject;
|
|
||||||
import yapion.hierarchy.types.YAPIONValue;
|
|
||||||
import yapion.parser.YAPIONParser;
|
|
||||||
import yapion.serializing.YAPIONDeserializer;
|
|
||||||
import yapion.serializing.YAPIONSerializer;
|
|
||||||
|
|
||||||
import java.util.HashSet;
|
import java.util.List;
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
@UtilityClass
|
@UtilityClass
|
||||||
public class DefaultHotbar {
|
public class DefaultHotbar {
|
||||||
|
|
||||||
private final YAPIONArray DEFAULT_HOTBAR = YAPIONParser.parse("{[{@type(org.bukkit.inventory.ItemStack)v(2230)type(WOODEN_AXE)meta{@type(org.bukkit.inventory.meta.ItemMeta)meta-type(UNSPECIFIC)display-name(WorldEdit Wand)lore{@type(java.util.ArrayList)values[Left click: select pos #1,Right click: select pos #2]}}},{@type(org.bukkit.inventory.ItemStack)v(2230)type(COMPASS)meta{@type(org.bukkit.inventory.meta.ItemMeta)meta-type(UNSPECIFIC)display-name(Navigation Wand)lore{@type(java.util.ArrayList)values[Left click: jump to location,Right click: pass through walls]}}},null,null,null,null,null,null,{@type(org.bukkit.inventory.ItemStack)v(2230)type(NETHER_STAR)meta{@type(org.bukkit.inventory.meta.ItemMeta)meta-type(UNSPECIFIC)display-name(§eBau GUI)}},null,null,{@type(org.bukkit.inventory.ItemStack)v(3117)type(ELYTRA)},null]}").getArray("");
|
|
||||||
|
|
||||||
public void updateHotbar(Player p) {
|
public void updateHotbar(Player p) {
|
||||||
ItemStack[] hotbar = new ItemStack[13];
|
ItemStack[] hotbar = new ItemStack[13];
|
||||||
System.arraycopy(p.getInventory().getContents(), 0, hotbar, 0, 9);
|
System.arraycopy(p.getInventory().getContents(), 0, hotbar, 0, 9);
|
||||||
System.arraycopy(p.getInventory().getArmorContents(), 0, hotbar, 9, 4);
|
System.arraycopy(p.getInventory().getArmorContents(), 0, hotbar, 9, 4);
|
||||||
YAPIONArray yapionArray = new YAPIONArray();
|
Config.STORE.get(p).setHotbar(copy(hotbar));
|
||||||
for (ItemStack itemStack : hotbar) {
|
Config.STORE.save(p);
|
||||||
if (itemStack != null) {
|
|
||||||
yapionArray.add(YAPIONSerializer.serialize(itemStack));
|
|
||||||
} else {
|
|
||||||
yapionArray.add(new YAPIONValue<>(null));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Config.getInstance().get(p).add("hotbar-" + Core.getVersion(), yapionArray);
|
|
||||||
Config.getInstance().save(p);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setHotbar(Player p) {
|
public void setHotbar(Player p) {
|
||||||
@@ -66,28 +50,34 @@ public class DefaultHotbar {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public ItemStack[] getItems(Player p) {
|
public ItemStack[] getItems(Player p) {
|
||||||
Config.getInstance().get(p).remap("hotbar", "hotbar-19");
|
return copy(Config.STORE.get(p).getHotbar());
|
||||||
YAPIONArray yapionArray = Config.getInstance().get(p).getYAPIONArrayOrSetDefault("hotbar-" + Core.getVersion(), defaultHotbar());
|
}
|
||||||
|
|
||||||
|
public ItemStack[] defaultHotbar() {
|
||||||
ItemStack[] hotbar = new ItemStack[13];
|
ItemStack[] hotbar = new ItemStack[13];
|
||||||
Set<Integer> invalid = new HashSet<>();
|
hotbar[0] = item(Material.WOODEN_AXE, "WorldEdit Wand", "Left click: select pos #1", "Right click: select pos #2");
|
||||||
yapionArray.forEach((integer, yapionAnyType) -> {
|
hotbar[1] = item(Material.COMPASS, "Navigation Wand", "Left click: jump to location", "Right click: pass through walls");
|
||||||
if (yapionAnyType instanceof YAPIONValue) {
|
hotbar[8] = item(Material.NETHER_STAR, "§eBau GUI");
|
||||||
hotbar[integer] = null;
|
hotbar[11] = new ItemStack(Material.ELYTRA);
|
||||||
} else {
|
|
||||||
try {
|
|
||||||
hotbar[integer] = YAPIONDeserializer.deserialize((YAPIONObject) yapionAnyType);
|
|
||||||
} catch (Exception e) {
|
|
||||||
invalid.add(integer);
|
|
||||||
hotbar[integer] = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
invalid.forEach(i -> yapionArray.set(i, new YAPIONValue<>(null)));
|
|
||||||
if (!invalid.isEmpty()) Config.getInstance().save(p);
|
|
||||||
return hotbar;
|
return hotbar;
|
||||||
}
|
}
|
||||||
|
|
||||||
public YAPIONArray defaultHotbar() {
|
private ItemStack item(Material material, String name, String... lore) {
|
||||||
return DEFAULT_HOTBAR;
|
ItemStack itemStack = new ItemStack(material);
|
||||||
|
ItemMeta meta = itemStack.getItemMeta();
|
||||||
|
meta.setDisplayName(name);
|
||||||
|
if (lore.length > 0) {
|
||||||
|
meta.setLore(List.of(lore));
|
||||||
|
}
|
||||||
|
itemStack.setItemMeta(meta);
|
||||||
|
return itemStack;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ItemStack[] copy(ItemStack[] hotbar) {
|
||||||
|
ItemStack[] copy = new ItemStack[13];
|
||||||
|
for (int i = 0; i < Math.min(hotbar.length, copy.length); i++) {
|
||||||
|
copy[i] = hotbar[i] == null ? null : hotbar[i].clone();
|
||||||
|
}
|
||||||
|
return copy;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -49,7 +49,7 @@ public class InventoryFillBauGuiItem extends BauGuiItem {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ItemStack getItem(Player player) {
|
public ItemStack getItem(Player player) {
|
||||||
String loreKey = Config.getInstance().get(player).getPlainValueOrDefault("inventoryfill", false) ? "OTHER_ITEMS_INVENTORY_FILL_LORE_ACTIVE" : "OTHER_ITEMS_INVENTORY_FILL_LORE_INACTIVE";
|
String loreKey = Config.STORE.get(player).isInventoryFill() ? "OTHER_ITEMS_INVENTORY_FILL_LORE_ACTIVE" : "OTHER_ITEMS_INVENTORY_FILL_LORE_INACTIVE";
|
||||||
return new SWItem(Material.HOPPER, BauSystem.MESSAGE.parse("OTHER_ITEMS_INVENTORY_FILL_NAME", player), Collections.singletonList(BauSystem.MESSAGE.parse(loreKey, player)), false, clickType -> {
|
return new SWItem(Material.HOPPER, BauSystem.MESSAGE.parse("OTHER_ITEMS_INVENTORY_FILL_NAME", player), Collections.singletonList(BauSystem.MESSAGE.parse(loreKey, player)), false, clickType -> {
|
||||||
}).getItemStack();
|
}).getItemStack();
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -40,7 +40,7 @@ public class InventoryFiller implements Listener {
|
|||||||
@EventHandler
|
@EventHandler
|
||||||
public void onPlayerDropItem(PlayerDropItemEvent event) {
|
public void onPlayerDropItem(PlayerDropItemEvent event) {
|
||||||
if (!Permission.BUILD.hasPermission(event.getPlayer())) return;
|
if (!Permission.BUILD.hasPermission(event.getPlayer())) return;
|
||||||
if (!Config.getInstance().get(event.getPlayer()).getPlainValueOrDefault("inventoryfill", false)) return;
|
if (!Config.STORE.get(event.getPlayer()).isInventoryFill()) return;
|
||||||
if (!event.getPlayer().isSneaking()) return;
|
if (!event.getPlayer().isSneaking()) return;
|
||||||
Block block = event.getPlayer().getTargetBlockExact(5);
|
Block block = event.getPlayer().getTargetBlockExact(5);
|
||||||
if (block == null) return;
|
if (block == null) return;
|
||||||
@@ -62,7 +62,7 @@ public class InventoryFiller implements Listener {
|
|||||||
@EventHandler
|
@EventHandler
|
||||||
public void onPlayerItemHeld(PlayerItemHeldEvent event) {
|
public void onPlayerItemHeld(PlayerItemHeldEvent event) {
|
||||||
if (!Permission.BUILD.hasPermission(event.getPlayer())) return;
|
if (!Permission.BUILD.hasPermission(event.getPlayer())) return;
|
||||||
if (!Config.getInstance().get(event.getPlayer()).getPlainValueOrDefault("inventoryfill", false)) return;
|
if (!Config.STORE.get(event.getPlayer()).isInventoryFill()) return;
|
||||||
if (!event.getPlayer().isSneaking()) return;
|
if (!event.getPlayer().isSneaking()) return;
|
||||||
ItemStack itemStack = event.getPlayer().getInventory().getItemInMainHand();
|
ItemStack itemStack = event.getPlayer().getInventory().getItemInMainHand();
|
||||||
if (itemStack.getType() == Material.AIR) return;
|
if (itemStack.getType() == Material.AIR) return;
|
||||||
|
|||||||
+2
-2
@@ -68,8 +68,8 @@ public class InventoryFillerCommand extends SWCommand {
|
|||||||
|
|
||||||
@Register(description = {"INVENTORY_FILL_HELP", "INVENTORY_FILL_INFO"})
|
@Register(description = {"INVENTORY_FILL_HELP", "INVENTORY_FILL_INFO"})
|
||||||
public void toggle(Player player) {
|
public void toggle(Player player) {
|
||||||
boolean inventoryFill = Config.getInstance().get(player).getPlainValueOrDefault("inventoryfill", false);
|
boolean inventoryFill = Config.STORE.get(player).isInventoryFill();
|
||||||
Config.getInstance().get(player).put("inventoryfill", !inventoryFill);
|
Config.STORE.get(player).setInventoryFill(!inventoryFill);
|
||||||
if (!inventoryFill) {
|
if (!inventoryFill) {
|
||||||
SWUtils.sendToActionbar(player, BauSystem.MESSAGE.parse("INVENTORY_FILL_ENABLE", player));
|
SWUtils.sendToActionbar(player, BauSystem.MESSAGE.parse("INVENTORY_FILL_ENABLE", player));
|
||||||
BauSystem.MESSAGE.send("INVENTORY_FILL_INFO", player);
|
BauSystem.MESSAGE.send("INVENTORY_FILL_INFO", player);
|
||||||
|
|||||||
+1
-1
@@ -46,7 +46,7 @@ public class ObserverTracerListener implements Listener {
|
|||||||
public ObserverTracerListener() {
|
public ObserverTracerListener() {
|
||||||
Bukkit.getScheduler().runTaskTimer(BauSystem.getInstance(), () -> {
|
Bukkit.getScheduler().runTaskTimer(BauSystem.getInstance(), () -> {
|
||||||
SWPlayer.allWithSingleComponent(ObserverTracer.class).forEach(pair -> {
|
SWPlayer.allWithSingleComponent(ObserverTracer.class).forEach(pair -> {
|
||||||
if (pair.getPlayer().getGameMode() != GameMode.SPECTATOR) return;
|
if (pair.getSwPlayer().getPlayer().getGameMode() != GameMode.SPECTATOR) return;
|
||||||
pair.getComponent().show();
|
pair.getComponent().show();
|
||||||
});
|
});
|
||||||
}, 15L, 15L);
|
}, 15L, 15L);
|
||||||
|
|||||||
+4
-4
@@ -167,7 +167,7 @@ public class RayVisualizerCommand extends SWCommand implements Listener {
|
|||||||
@EventHandler
|
@EventHandler
|
||||||
public void onPlayerJoin(PlayerJoinEvent event) {
|
public void onPlayerJoin(PlayerJoinEvent event) {
|
||||||
if (!Permission.BUILD.hasPermission(event.getPlayer())) return;
|
if (!Permission.BUILD.hasPermission(event.getPlayer())) return;
|
||||||
boolean rayvisualizer = Config.getInstance().get(event.getPlayer()).getPlainValueOrDefault("rayvisualizer", false);
|
boolean rayvisualizer = Config.STORE.get(event.getPlayer()).isRayVisualizer();
|
||||||
if (rayvisualizer) server.addPlayer(event.getPlayer());
|
if (rayvisualizer) server.addPlayer(event.getPlayer());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -181,7 +181,7 @@ public class RayVisualizerCommand extends SWCommand implements Listener {
|
|||||||
Bukkit.getScheduler().runTaskLater(BauSystem.getInstance(), () -> {
|
Bukkit.getScheduler().runTaskLater(BauSystem.getInstance(), () -> {
|
||||||
for (Player player : Bukkit.getOnlinePlayers()) {
|
for (Player player : Bukkit.getOnlinePlayers()) {
|
||||||
if (Permission.BUILD.hasPermission(player)) {
|
if (Permission.BUILD.hasPermission(player)) {
|
||||||
boolean rayvisualizer = Config.getInstance().get(player).getPlainValueOrDefault("rayvisualizer", false);
|
boolean rayvisualizer = Config.STORE.get(player).isRayVisualizer();
|
||||||
if (rayvisualizer) server.addPlayer(player);
|
if (rayvisualizer) server.addPlayer(player);
|
||||||
} else {
|
} else {
|
||||||
server.removePlayer(player);
|
server.removePlayer(player);
|
||||||
@@ -192,8 +192,8 @@ public class RayVisualizerCommand extends SWCommand implements Listener {
|
|||||||
|
|
||||||
@Register
|
@Register
|
||||||
public void toggle(@Validator Player player) {
|
public void toggle(@Validator Player player) {
|
||||||
boolean rayvisualizer = Config.getInstance().get(player).getPlainValueOrDefault("rayvisualizer", false);
|
boolean rayvisualizer = Config.STORE.get(player).isRayVisualizer();
|
||||||
Config.getInstance().get(player).put("rayvisualizer", !rayvisualizer);
|
Config.STORE.get(player).setRayVisualizer(!rayvisualizer);
|
||||||
if (!rayvisualizer) {
|
if (!rayvisualizer) {
|
||||||
SWUtils.sendToActionbar(player, BauSystem.MESSAGE.parse("RAY_VISUALIZER_ENABLE", player));
|
SWUtils.sendToActionbar(player, BauSystem.MESSAGE.parse("RAY_VISUALIZER_ENABLE", player));
|
||||||
server.addPlayer(player);
|
server.addPlayer(player);
|
||||||
|
|||||||
+3
-3
@@ -21,6 +21,7 @@ package de.steamwar.bausystem.features.simulator.data;
|
|||||||
|
|
||||||
import de.steamwar.bausystem.features.simulator.execute.SimulatorAction;
|
import de.steamwar.bausystem.features.simulator.execute.SimulatorAction;
|
||||||
import de.steamwar.bausystem.features.simulator.gui.base.SimulatorBaseGui;
|
import de.steamwar.bausystem.features.simulator.gui.base.SimulatorBaseGui;
|
||||||
|
import de.steamwar.bausystem.features.simulator.storage.SimulatorConfig;
|
||||||
import de.steamwar.inventory.InvCallback;
|
import de.steamwar.inventory.InvCallback;
|
||||||
import de.steamwar.inventory.SWItem;
|
import de.steamwar.inventory.SWItem;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
@@ -28,7 +29,6 @@ import lombok.Setter;
|
|||||||
import org.bukkit.Material;
|
import org.bukkit.Material;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
import org.bukkit.util.Vector;
|
import org.bukkit.util.Vector;
|
||||||
import yapion.hierarchy.types.YAPIONObject;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
@@ -121,9 +121,9 @@ public abstract class SimulatorElement<T extends SimulatorPhase> {
|
|||||||
|
|
||||||
public abstract String getType();
|
public abstract String getType();
|
||||||
|
|
||||||
public void saveExtra(YAPIONObject elementObject) {
|
public void saveExtra(SimulatorConfig.Element element) {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void loadExtra(YAPIONObject elementObject) {
|
public void loadExtra(SimulatorConfig.Element element) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-3
@@ -20,10 +20,10 @@
|
|||||||
package de.steamwar.bausystem.features.simulator.data;
|
package de.steamwar.bausystem.features.simulator.data;
|
||||||
|
|
||||||
import de.steamwar.bausystem.features.simulator.execute.SimulatorAction;
|
import de.steamwar.bausystem.features.simulator.execute.SimulatorAction;
|
||||||
|
import de.steamwar.bausystem.features.simulator.storage.SimulatorConfig;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
import org.bukkit.util.Vector;
|
import org.bukkit.util.Vector;
|
||||||
import yapion.hierarchy.types.YAPIONObject;
|
|
||||||
|
|
||||||
import java.util.function.BiConsumer;
|
import java.util.function.BiConsumer;
|
||||||
|
|
||||||
@@ -38,9 +38,9 @@ public abstract class SimulatorPhase {
|
|||||||
|
|
||||||
public abstract void toSimulatorActions(Vector position, BiConsumer<Integer, SimulatorAction> tickStart, BiConsumer<Integer, SimulatorAction> tickEnd);
|
public abstract void toSimulatorActions(Vector position, BiConsumer<Integer, SimulatorAction> tickStart, BiConsumer<Integer, SimulatorAction> tickEnd);
|
||||||
|
|
||||||
public void saveExtra(YAPIONObject phaseObject) {
|
public void saveExtra(SimulatorConfig.Phase phase) {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void loadExtra(YAPIONObject phaseObject) {
|
public void loadExtra(SimulatorConfig.Phase phase) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+5
-5
@@ -21,6 +21,7 @@ package de.steamwar.bausystem.features.simulator.data.observer;
|
|||||||
|
|
||||||
import de.steamwar.bausystem.features.simulator.data.SimulatorPhase;
|
import de.steamwar.bausystem.features.simulator.data.SimulatorPhase;
|
||||||
import de.steamwar.bausystem.features.simulator.execute.SimulatorAction;
|
import de.steamwar.bausystem.features.simulator.execute.SimulatorAction;
|
||||||
|
import de.steamwar.bausystem.features.simulator.storage.SimulatorConfig;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.NoArgsConstructor;
|
import lombok.NoArgsConstructor;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
@@ -31,7 +32,6 @@ import org.bukkit.block.BlockFace;
|
|||||||
import org.bukkit.block.BlockState;
|
import org.bukkit.block.BlockState;
|
||||||
import org.bukkit.block.data.type.Observer;
|
import org.bukkit.block.data.type.Observer;
|
||||||
import org.bukkit.util.Vector;
|
import org.bukkit.util.Vector;
|
||||||
import yapion.hierarchy.types.YAPIONObject;
|
|
||||||
|
|
||||||
import java.util.function.BiConsumer;
|
import java.util.function.BiConsumer;
|
||||||
|
|
||||||
@@ -70,12 +70,12 @@ public final class ObserverPhase extends SimulatorPhase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void saveExtra(YAPIONObject phaseObject) {
|
public void saveExtra(SimulatorConfig.Phase phase) {
|
||||||
phaseObject.add("orientation", orientation.name());
|
phase.setOrientation(orientation);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void loadExtra(YAPIONObject phaseObject) {
|
public void loadExtra(SimulatorConfig.Phase phase) {
|
||||||
orientation = BlockFace.valueOf(phaseObject.getPlainValue("orientation"));
|
if (phase.getOrientation() != null) orientation = phase.getOrientation();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+7
-7
@@ -24,13 +24,13 @@ import de.steamwar.bausystem.features.simulator.data.SimulatorElement;
|
|||||||
import de.steamwar.bausystem.features.simulator.data.SimulatorGroup;
|
import de.steamwar.bausystem.features.simulator.data.SimulatorGroup;
|
||||||
import de.steamwar.bausystem.features.simulator.gui.SimulatorTNTGui;
|
import de.steamwar.bausystem.features.simulator.gui.SimulatorTNTGui;
|
||||||
import de.steamwar.bausystem.features.simulator.gui.base.SimulatorBaseGui;
|
import de.steamwar.bausystem.features.simulator.gui.base.SimulatorBaseGui;
|
||||||
|
import de.steamwar.bausystem.features.simulator.storage.SimulatorConfig;
|
||||||
import de.steamwar.inventory.InvCallback;
|
import de.steamwar.inventory.InvCallback;
|
||||||
import de.steamwar.inventory.SWItem;
|
import de.steamwar.inventory.SWItem;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import org.bukkit.Material;
|
import org.bukkit.Material;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
import org.bukkit.util.Vector;
|
import org.bukkit.util.Vector;
|
||||||
import yapion.hierarchy.types.YAPIONObject;
|
|
||||||
|
|
||||||
public final class TNTElement extends SimulatorElement<TNTPhase> {
|
public final class TNTElement extends SimulatorElement<TNTPhase> {
|
||||||
|
|
||||||
@@ -100,14 +100,14 @@ public final class TNTElement extends SimulatorElement<TNTPhase> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void saveExtra(YAPIONObject elementObject) {
|
public void saveExtra(SimulatorConfig.Element element) {
|
||||||
elementObject.add("alignmentX", alignment.getX());
|
element.setAlignmentX(alignment.getX());
|
||||||
elementObject.add("alignmentZ", alignment.getZ());
|
element.setAlignmentZ(alignment.getZ());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void loadExtra(YAPIONObject elementObject) {
|
public void loadExtra(SimulatorConfig.Element element) {
|
||||||
alignment.setX(elementObject.getDoubleOrSetDefault("alignmentX", 0));
|
alignment.setX(element.getAlignmentX() != null ? element.getAlignmentX() : 0);
|
||||||
alignment.setZ(elementObject.getDoubleOrSetDefault("alignmentZ", 0));
|
alignment.setZ(element.getAlignmentZ() != null ? element.getAlignmentZ() : 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+11
-11
@@ -21,6 +21,7 @@ package de.steamwar.bausystem.features.simulator.data.tnt;
|
|||||||
|
|
||||||
import de.steamwar.bausystem.features.simulator.data.SimulatorPhase;
|
import de.steamwar.bausystem.features.simulator.data.SimulatorPhase;
|
||||||
import de.steamwar.bausystem.features.simulator.execute.SimulatorAction;
|
import de.steamwar.bausystem.features.simulator.execute.SimulatorAction;
|
||||||
|
import de.steamwar.bausystem.features.simulator.storage.SimulatorConfig;
|
||||||
import de.steamwar.bausystem.region.Region;
|
import de.steamwar.bausystem.region.Region;
|
||||||
import de.steamwar.bausystem.region.flags.Flag;
|
import de.steamwar.bausystem.region.flags.Flag;
|
||||||
import de.steamwar.bausystem.region.flags.FreezeMode;
|
import de.steamwar.bausystem.region.flags.FreezeMode;
|
||||||
@@ -31,7 +32,6 @@ import org.bukkit.Location;
|
|||||||
import org.bukkit.World;
|
import org.bukkit.World;
|
||||||
import org.bukkit.entity.TNTPrimed;
|
import org.bukkit.entity.TNTPrimed;
|
||||||
import org.bukkit.util.Vector;
|
import org.bukkit.util.Vector;
|
||||||
import yapion.hierarchy.types.YAPIONObject;
|
|
||||||
|
|
||||||
import java.util.function.BiConsumer;
|
import java.util.function.BiConsumer;
|
||||||
|
|
||||||
@@ -83,18 +83,18 @@ public final class TNTPhase extends SimulatorPhase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void saveExtra(YAPIONObject phaseObject) {
|
public void saveExtra(SimulatorConfig.Phase phase) {
|
||||||
phaseObject.add("count", count);
|
phase.setCount(count);
|
||||||
phaseObject.add("xJump", xJump);
|
phase.setXJump(xJump);
|
||||||
phaseObject.add("yJump", yJump);
|
phase.setYJump(yJump);
|
||||||
phaseObject.add("zJump", zJump);
|
phase.setZJump(zJump);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void loadExtra(YAPIONObject phaseObject) {
|
public void loadExtra(SimulatorConfig.Phase phase) {
|
||||||
count = phaseObject.getPlainValue("count");
|
if (phase.getCount() != null) count = phase.getCount();
|
||||||
xJump = phaseObject.getPlainValue("xJump");
|
if (phase.getXJump() != null) xJump = phase.getXJump();
|
||||||
yJump = phaseObject.getPlainValue("yJump");
|
if (phase.getYJump() != null) yJump = phase.getYJump();
|
||||||
zJump = phaseObject.getPlainValue("zJump");
|
if (phase.getZJump() != null) zJump = phase.getZJump();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+29
-37
@@ -19,6 +19,7 @@
|
|||||||
|
|
||||||
package de.steamwar.bausystem.features.simulator.storage;
|
package de.steamwar.bausystem.features.simulator.storage;
|
||||||
|
|
||||||
|
import com.google.gson.JsonParseException;
|
||||||
import de.steamwar.bausystem.features.simulator.data.Simulator;
|
import de.steamwar.bausystem.features.simulator.data.Simulator;
|
||||||
import de.steamwar.bausystem.features.simulator.data.SimulatorElement;
|
import de.steamwar.bausystem.features.simulator.data.SimulatorElement;
|
||||||
import de.steamwar.bausystem.features.simulator.data.SimulatorGroup;
|
import de.steamwar.bausystem.features.simulator.data.SimulatorGroup;
|
||||||
@@ -29,12 +30,7 @@ import de.steamwar.bausystem.features.simulator.data.redstone.RedstoneElement;
|
|||||||
import de.steamwar.bausystem.features.simulator.data.redstone.RedstonePhase;
|
import de.steamwar.bausystem.features.simulator.data.redstone.RedstonePhase;
|
||||||
import de.steamwar.bausystem.features.simulator.data.tnt.TNTElement;
|
import de.steamwar.bausystem.features.simulator.data.tnt.TNTElement;
|
||||||
import de.steamwar.bausystem.features.simulator.data.tnt.TNTPhase;
|
import de.steamwar.bausystem.features.simulator.data.tnt.TNTPhase;
|
||||||
import org.bukkit.Material;
|
|
||||||
import org.bukkit.util.Vector;
|
import org.bukkit.util.Vector;
|
||||||
import yapion.exceptions.YAPIONException;
|
|
||||||
import yapion.hierarchy.types.YAPIONArray;
|
|
||||||
import yapion.hierarchy.types.YAPIONObject;
|
|
||||||
import yapion.parser.YAPIONParser;
|
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@@ -51,44 +47,41 @@ public class SimFormatSimulatorLoader implements SimulatorLoader {
|
|||||||
return Optional.empty();
|
return Optional.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
YAPIONObject yapionObject;
|
SimulatorConfig config;
|
||||||
try {
|
try {
|
||||||
yapionObject = YAPIONParser.parse(file);
|
config = SimulatorConfig.STORE.load(file);
|
||||||
} catch (YAPIONException | IOException e) {
|
} catch (JsonParseException | IllegalStateException | IOException e) {
|
||||||
return Optional.empty();
|
return Optional.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
String name = file.getName().substring(0, file.getName().length() - ".sim".length());
|
String name = file.getName().substring(0, file.getName().length() - ".sim".length());
|
||||||
|
|
||||||
Simulator simulator = new Simulator(name);
|
Simulator simulator = new Simulator(name);
|
||||||
loadSimulator(yapionObject, simulator);
|
loadSimulator(config, simulator);
|
||||||
return Optional.of(Collections.singletonList(simulator));
|
return Optional.of(Collections.singletonList(simulator));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void loadSimulator(YAPIONObject simulatorObject, Simulator simulator) {
|
private void loadSimulator(SimulatorConfig config, Simulator simulator) {
|
||||||
simulator.setMaterial(Material.valueOf(simulatorObject.getPlainValue("material")));
|
if (config.getMaterial() != null) simulator.setMaterial(config.getMaterial());
|
||||||
simulator.setAutoTrace(simulatorObject.getPlainValue("autoTrace"));
|
simulator.setAutoTrace(config.isAutoTrace());
|
||||||
simulator.setAutoTestblock(simulatorObject.getPlainValueOrDefault("autoTestblock", false));
|
simulator.setAutoTestblock(config.isAutoTestblock());
|
||||||
|
|
||||||
YAPIONArray groups = simulatorObject.getArray("groups");
|
config.getGroups().forEach(groupConfig -> {
|
||||||
groups.streamObject().forEach(groupObject -> {
|
|
||||||
SimulatorGroup simulatorGroup = new SimulatorGroup();
|
SimulatorGroup simulatorGroup = new SimulatorGroup();
|
||||||
loadGroup(groupObject, simulatorGroup);
|
loadGroup(groupConfig, simulatorGroup);
|
||||||
simulator.add(simulatorGroup);
|
simulator.add(simulatorGroup);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void loadGroup(YAPIONObject groupObject, SimulatorGroup group) {
|
private void loadGroup(SimulatorConfig.Group groupConfig, SimulatorGroup group) {
|
||||||
group.setMaterial(Material.valueOf(groupObject.getPlainValue("material")));
|
if (groupConfig.getMaterial() != null) group.setMaterial(groupConfig.getMaterial());
|
||||||
group.setDisabled(groupObject.getPlainValue("disabled"));
|
group.setDisabled(groupConfig.isDisabled());
|
||||||
|
|
||||||
YAPIONArray elements = groupObject.getArray("elements");
|
groupConfig.getElements().forEach(elementConfig -> {
|
||||||
elements.streamObject().forEach(elementObject -> {
|
|
||||||
SimulatorElement<?> element;
|
SimulatorElement<?> element;
|
||||||
Supplier<SimulatorPhase> phaseConstructor;
|
Supplier<SimulatorPhase> phaseConstructor;
|
||||||
Vector position = new Vector(elementObject.getDouble("x"), elementObject.getDouble("y"), elementObject.getDouble("z"));
|
Vector position = new Vector(elementConfig.getX(), elementConfig.getY(), elementConfig.getZ());
|
||||||
String type = elementObject.getPlainValue("type");
|
switch (String.valueOf(elementConfig.getType())) {
|
||||||
switch (type) {
|
|
||||||
case "TNT":
|
case "TNT":
|
||||||
element = new TNTElement(position);
|
element = new TNTElement(position);
|
||||||
phaseConstructor = TNTPhase::new;
|
phaseConstructor = TNTPhase::new;
|
||||||
@@ -109,28 +102,27 @@ public class SimFormatSimulatorLoader implements SimulatorLoader {
|
|||||||
if (element == null) {
|
if (element == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
loadElement(elementObject, element, phaseConstructor);
|
loadElement(elementConfig, element, phaseConstructor);
|
||||||
group.add(element);
|
group.add(element);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void loadElement(YAPIONObject elementObject, SimulatorElement<?> element, Supplier<SimulatorPhase> phaseConstructor) {
|
private void loadElement(SimulatorConfig.Element elementConfig, SimulatorElement<?> element, Supplier<SimulatorPhase> phaseConstructor) {
|
||||||
element.setMaterial(Material.valueOf(elementObject.getPlainValue("material")));
|
if (elementConfig.getMaterial() != null) element.setMaterial(elementConfig.getMaterial());
|
||||||
element.setDisabled(elementObject.getPlainValue("disabled"));
|
element.setDisabled(elementConfig.isDisabled());
|
||||||
element.loadExtra(elementObject);
|
element.loadExtra(elementConfig);
|
||||||
|
|
||||||
YAPIONArray phases = elementObject.getArray("phases");
|
elementConfig.getPhases().forEach(phaseConfig -> {
|
||||||
phases.streamObject().forEach(phaseObject -> {
|
|
||||||
SimulatorPhase phase = phaseConstructor.get();
|
SimulatorPhase phase = phaseConstructor.get();
|
||||||
loadPhase(phaseObject, phase);
|
loadPhase(phaseConfig, phase);
|
||||||
((SimulatorElement) element).add(phase);
|
((SimulatorElement) element).add(phase);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void loadPhase(YAPIONObject phaseObject, SimulatorPhase phase) {
|
private void loadPhase(SimulatorConfig.Phase phaseConfig, SimulatorPhase phase) {
|
||||||
phase.setTickOffset(phaseObject.getPlainValue("tickOffset"));
|
phase.setTickOffset(phaseConfig.getTickOffset());
|
||||||
phase.setLifetime(phaseObject.getPlainValue("lifetime"));
|
phase.setLifetime(phaseConfig.getLifetime());
|
||||||
phase.setOrder(phaseObject.getPlainValue("order"));
|
phase.setOrder(phaseConfig.getOrder());
|
||||||
phase.loadExtra(phaseObject);
|
phase.loadExtra(phaseConfig);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+117
@@ -0,0 +1,117 @@
|
|||||||
|
/*
|
||||||
|
* This file is a part of the SteamWar software.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2026 SteamWar.de-Serverteam
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program 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 Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.steamwar.bausystem.features.simulator.storage;
|
||||||
|
|
||||||
|
import de.steamwar.bausystem.config.ConfigStore;
|
||||||
|
import de.steamwar.bausystem.config.VersionedConfig;
|
||||||
|
import lombok.AccessLevel;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
import org.bukkit.Material;
|
||||||
|
import org.bukkit.block.BlockFace;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The current stored form of a simulator, saved as JSON in a .sim file. Files
|
||||||
|
* saved before the version was stored have no version and are version 1.
|
||||||
|
* Fields missing in the stored JSON keep the default value of their initializer,
|
||||||
|
* so adding a new field does not need a new version.
|
||||||
|
* <p>
|
||||||
|
* When a field gets removed, renamed or its meaning changes, copy this class
|
||||||
|
* to SimulatorConfigV{@link #VERSION} (without the {@link #STORE}), increase {@link #VERSION}, write a
|
||||||
|
* {@link de.steamwar.bausystem.config.ConfigConverter} from the copy to
|
||||||
|
* this class and register it in the {@link #STORE}.
|
||||||
|
*/
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
public class SimulatorConfig implements VersionedConfig {
|
||||||
|
|
||||||
|
public static final int VERSION = 1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Files saved before the version was stored are version 1
|
||||||
|
*/
|
||||||
|
public static final ConfigStore<SimulatorConfig> STORE = new ConfigStore<>(SimulatorConfig.class, VERSION, SimulatorConfig::new)
|
||||||
|
.unversioned(1);
|
||||||
|
|
||||||
|
@Setter(AccessLevel.NONE)
|
||||||
|
protected int version = VERSION;
|
||||||
|
|
||||||
|
protected Material material = Material.BARREL;
|
||||||
|
protected boolean autoTrace = false;
|
||||||
|
protected boolean autoTestblock = false;
|
||||||
|
protected List<Group> groups = new ArrayList<>();
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
public static class Group {
|
||||||
|
|
||||||
|
protected Material material = Material.CHEST;
|
||||||
|
protected boolean disabled = false;
|
||||||
|
protected List<Element> elements = new ArrayList<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
public static class Element {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* One of "TNT", "Redstone" or "Observer"
|
||||||
|
*/
|
||||||
|
protected String type;
|
||||||
|
protected Material material;
|
||||||
|
protected boolean disabled = false;
|
||||||
|
protected double x;
|
||||||
|
protected double y;
|
||||||
|
protected double z;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Only used by TNT
|
||||||
|
*/
|
||||||
|
protected Double alignmentX;
|
||||||
|
protected Double alignmentZ;
|
||||||
|
|
||||||
|
protected List<Phase> phases = new ArrayList<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
public static class Phase {
|
||||||
|
|
||||||
|
protected int tickOffset;
|
||||||
|
protected int lifetime;
|
||||||
|
protected int order;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Only used by TNT
|
||||||
|
*/
|
||||||
|
protected Integer count;
|
||||||
|
protected Boolean xJump;
|
||||||
|
protected Boolean yJump;
|
||||||
|
protected Boolean zJump;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Only used by Observer
|
||||||
|
*/
|
||||||
|
protected BlockFace orientation;
|
||||||
|
}
|
||||||
|
}
|
||||||
+43
-35
@@ -19,24 +19,30 @@
|
|||||||
|
|
||||||
package de.steamwar.bausystem.features.simulator.storage;
|
package de.steamwar.bausystem.features.simulator.storage;
|
||||||
|
|
||||||
|
import de.steamwar.bausystem.config.legacy.YapionReader;
|
||||||
import de.steamwar.bausystem.features.simulator.data.Simulator;
|
import de.steamwar.bausystem.features.simulator.data.Simulator;
|
||||||
import de.steamwar.bausystem.features.simulator.data.SimulatorGroup;
|
import de.steamwar.bausystem.features.simulator.data.SimulatorGroup;
|
||||||
import de.steamwar.bausystem.features.simulator.data.tnt.TNTElement;
|
import de.steamwar.bausystem.features.simulator.data.tnt.TNTElement;
|
||||||
import de.steamwar.bausystem.features.simulator.data.tnt.TNTPhase;
|
import de.steamwar.bausystem.features.simulator.data.tnt.TNTPhase;
|
||||||
import org.bukkit.Material;
|
import org.bukkit.Material;
|
||||||
import org.bukkit.util.Vector;
|
import org.bukkit.util.Vector;
|
||||||
import yapion.exceptions.YAPIONException;
|
|
||||||
import yapion.hierarchy.types.YAPIONArray;
|
|
||||||
import yapion.hierarchy.types.YAPIONObject;
|
|
||||||
import yapion.parser.YAPIONParser;
|
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.nio.file.Files;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
|
import static de.steamwar.bausystem.config.legacy.YapionReader.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads the legacy .simulator files stored as YAPION, they are deleted after loading
|
||||||
|
* and saved in the current format by the {@link SimulatorSaver}.
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
public class SimulatorFormatSimulatorLoader implements SimulatorLoader {
|
public class SimulatorFormatSimulatorLoader implements SimulatorLoader {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -45,20 +51,19 @@ public class SimulatorFormatSimulatorLoader implements SimulatorLoader {
|
|||||||
return Optional.empty();
|
return Optional.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
YAPIONObject yapionObject;
|
Map<String, Object> yapionObject;
|
||||||
try {
|
try {
|
||||||
yapionObject = YAPIONParser.parse(file);
|
yapionObject = YapionReader.parse(Files.readString(file.toPath(), StandardCharsets.UTF_8));
|
||||||
} catch (YAPIONException | IOException e) {
|
} catch (IllegalArgumentException | IOException e) {
|
||||||
return Optional.empty();
|
return Optional.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
String name = file.getName().substring(0, file.getName().length() - ".simulator".length());
|
String name = file.getName().substring(0, file.getName().length() - ".simulator".length());
|
||||||
|
|
||||||
Simulator simulator = new Simulator(name);
|
Simulator simulator = new Simulator(name);
|
||||||
simulator.setMaterial(Material.valueOf(yapionObject.getStringOrDefault("material", "BARREL")));
|
simulator.setMaterial(Material.valueOf(getString(yapionObject, "material", "BARREL")));
|
||||||
|
|
||||||
YAPIONArray tntElements = yapionObject.getArrayOrDefault("tntElements", new YAPIONArray());
|
for (Map<?, ?> element : getObjects(yapionObject, "tntElements")) {
|
||||||
for (YAPIONObject element : tntElements.streamObject().collect(Collectors.toList())) {
|
|
||||||
if (element.containsKey("elements")) {
|
if (element.containsKey("elements")) {
|
||||||
simulator.add(loadGroup(element));
|
simulator.add(loadGroup(element));
|
||||||
} else {
|
} else {
|
||||||
@@ -70,27 +75,26 @@ public class SimulatorFormatSimulatorLoader implements SimulatorLoader {
|
|||||||
return Optional.of(Collections.singletonList(simulator));
|
return Optional.of(Collections.singletonList(simulator));
|
||||||
}
|
}
|
||||||
|
|
||||||
private SimulatorGroup loadGroup(YAPIONObject element) {
|
private SimulatorGroup loadGroup(Map<?, ?> element) {
|
||||||
double x = element.getDoubleOrDefault("x", 0);
|
double x = getDouble(element, "x", 0);
|
||||||
double y = element.getDoubleOrDefault("y", 0);
|
double y = getDouble(element, "y", 0);
|
||||||
double z = element.getDoubleOrDefault("z", 0);
|
double z = getDouble(element, "z", 0);
|
||||||
int tickOffset = element.getIntOrDefault("tickOffset", 0);
|
int tickOffset = getInt(element, "tickOffset", 0);
|
||||||
|
|
||||||
SimulatorGroup simulatorGroup = new SimulatorGroup();
|
SimulatorGroup simulatorGroup = new SimulatorGroup();
|
||||||
simulatorGroup.setDisabled(element.getBooleanOrDefault("disabled", false));
|
simulatorGroup.setDisabled(getBoolean(element, "disabled", false));
|
||||||
|
|
||||||
YAPIONArray elements = element.getArrayOrDefault("elements", new YAPIONArray());
|
for (Map<?, ?> e : getObjects(element, "elements")) {
|
||||||
for (YAPIONObject e : elements.streamObject().collect(Collectors.toList())) {
|
|
||||||
simulatorGroup.add(loadElement(e, x, y, z, tickOffset));
|
simulatorGroup.add(loadElement(e, x, y, z, tickOffset));
|
||||||
}
|
}
|
||||||
|
|
||||||
return simulatorGroup;
|
return simulatorGroup;
|
||||||
}
|
}
|
||||||
|
|
||||||
private TNTElement loadElement(YAPIONObject element, double dx, double dy, double dz, int dTickOffset) {
|
private TNTElement loadElement(Map<?, ?> element, double dx, double dy, double dz, int dTickOffset) {
|
||||||
TNTElement tntElement = new TNTElement(new Vector(element.getDouble("x") + dx, element.getDouble("y") + dy, element.getDouble("z") + dz));
|
TNTElement tntElement = new TNTElement(new Vector(requireDouble(element, "x") + dx, requireDouble(element, "y") + dy, requireDouble(element, "z") + dz));
|
||||||
tntElement.setDisabled(element.getBooleanOrDefault("disabled", false));
|
tntElement.setDisabled(getBoolean(element, "disabled", false));
|
||||||
tntElement.setMaterial(Material.valueOf(element.getStringOrDefault("material", "TNT")));
|
tntElement.setMaterial(Material.valueOf(getString(element, "material", "TNT")));
|
||||||
|
|
||||||
TNTPhase tntPhase = loadPhase(element);
|
TNTPhase tntPhase = loadPhase(element);
|
||||||
tntPhase.setTickOffset(tntPhase.getTickOffset() + dTickOffset);
|
tntPhase.setTickOffset(tntPhase.getTickOffset() + dTickOffset);
|
||||||
@@ -99,12 +103,12 @@ public class SimulatorFormatSimulatorLoader implements SimulatorLoader {
|
|||||||
return tntElement;
|
return tntElement;
|
||||||
}
|
}
|
||||||
|
|
||||||
private SimulatorGroup loadElement(YAPIONObject element) {
|
private SimulatorGroup loadElement(Map<?, ?> element) {
|
||||||
SimulatorGroup simulatorGroup = new SimulatorGroup();
|
SimulatorGroup simulatorGroup = new SimulatorGroup();
|
||||||
|
|
||||||
TNTElement tntElement = new TNTElement(new Vector(element.getDouble("x"), element.getDouble("y"), element.getDouble("z")));
|
TNTElement tntElement = new TNTElement(new Vector(requireDouble(element, "x"), requireDouble(element, "y"), requireDouble(element, "z")));
|
||||||
tntElement.setDisabled(element.getBooleanOrDefault("disabled", false));
|
tntElement.setDisabled(getBoolean(element, "disabled", false));
|
||||||
tntElement.setMaterial(Material.valueOf(element.getStringOrDefault("material", "TNT")));
|
tntElement.setMaterial(Material.valueOf(getString(element, "material", "TNT")));
|
||||||
simulatorGroup.add(tntElement);
|
simulatorGroup.add(tntElement);
|
||||||
|
|
||||||
tntElement.add(loadPhase(element));
|
tntElement.add(loadPhase(element));
|
||||||
@@ -112,15 +116,15 @@ public class SimulatorFormatSimulatorLoader implements SimulatorLoader {
|
|||||||
return simulatorGroup;
|
return simulatorGroup;
|
||||||
}
|
}
|
||||||
|
|
||||||
private TNTPhase loadPhase(YAPIONObject element) {
|
private TNTPhase loadPhase(Map<?, ?> element) {
|
||||||
TNTPhase tntPhase = new TNTPhase();
|
TNTPhase tntPhase = new TNTPhase();
|
||||||
tntPhase.setLifetime(element.getIntOrDefault("fuseTicks", 80));
|
tntPhase.setLifetime(getInt(element, "fuseTicks", 80));
|
||||||
tntPhase.setCount(element.getIntOrDefault("count", 1));
|
tntPhase.setCount(getInt(element, "count", 1));
|
||||||
tntPhase.setTickOffset(element.getIntOrDefault("tickOffset", 0));
|
tntPhase.setTickOffset(getInt(element, "tickOffset", 0));
|
||||||
tntPhase.setXJump(element.getBooleanOrDefault("xVelocity", false));
|
tntPhase.setXJump(getBoolean(element, "xVelocity", false));
|
||||||
tntPhase.setYJump(element.getBooleanOrDefault("yVelocity", false));
|
tntPhase.setYJump(getBoolean(element, "yVelocity", false));
|
||||||
tntPhase.setZJump(element.getBooleanOrDefault("zVelocity", false));
|
tntPhase.setZJump(getBoolean(element, "zVelocity", false));
|
||||||
switch (element.getStringOrDefault("order", "REPEATER")) {
|
switch (getString(element, "order", "REPEATER")) {
|
||||||
case "REPEATER":
|
case "REPEATER":
|
||||||
tntPhase.setOrder(0);
|
tntPhase.setOrder(0);
|
||||||
break;
|
break;
|
||||||
@@ -133,4 +137,8 @@ public class SimulatorFormatSimulatorLoader implements SimulatorLoader {
|
|||||||
}
|
}
|
||||||
return tntPhase;
|
return tntPhase;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private double requireDouble(Map<?, ?> element, String key) {
|
||||||
|
return ((Number) element.get(key)).doubleValue();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+24
-33
@@ -21,9 +21,6 @@ package de.steamwar.bausystem.features.simulator.storage;
|
|||||||
|
|
||||||
import de.steamwar.bausystem.features.simulator.data.Simulator;
|
import de.steamwar.bausystem.features.simulator.data.Simulator;
|
||||||
import lombok.experimental.UtilityClass;
|
import lombok.experimental.UtilityClass;
|
||||||
import yapion.hierarchy.output.FileOutput;
|
|
||||||
import yapion.hierarchy.types.YAPIONArray;
|
|
||||||
import yapion.hierarchy.types.YAPIONObject;
|
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@@ -36,48 +33,42 @@ public class SimulatorSaver {
|
|||||||
directory.mkdirs();
|
directory.mkdirs();
|
||||||
}
|
}
|
||||||
|
|
||||||
YAPIONObject simulatorObject = new YAPIONObject();
|
SimulatorConfig config = new SimulatorConfig();
|
||||||
simulatorObject.add("material", simulator.getMaterial().name());
|
config.setMaterial(simulator.getMaterial());
|
||||||
simulatorObject.add("autoTrace", simulator.isAutoTrace());
|
config.setAutoTrace(simulator.isAutoTrace());
|
||||||
simulatorObject.add("autoTestblock", simulator.isAutoTestblock());
|
config.setAutoTestblock(simulator.isAutoTestblock());
|
||||||
|
|
||||||
YAPIONArray groups = new YAPIONArray();
|
|
||||||
simulator.getGroups().forEach(group -> {
|
simulator.getGroups().forEach(group -> {
|
||||||
YAPIONObject groupObject = new YAPIONObject();
|
SimulatorConfig.Group groupConfig = new SimulatorConfig.Group();
|
||||||
groupObject.add("material", group.getMaterial().name());
|
groupConfig.setMaterial(group.getMaterial());
|
||||||
groupObject.add("disabled", group.isDisabled());
|
groupConfig.setDisabled(group.isDisabled());
|
||||||
|
|
||||||
YAPIONArray elements = new YAPIONArray();
|
|
||||||
group.getElements().forEach(element -> {
|
group.getElements().forEach(element -> {
|
||||||
YAPIONObject elementObject = new YAPIONObject();
|
SimulatorConfig.Element elementConfig = new SimulatorConfig.Element();
|
||||||
elementObject.add("type", element.getType());
|
elementConfig.setType(element.getType());
|
||||||
elementObject.add("material", element.getMaterial().name());
|
elementConfig.setMaterial(element.getMaterial());
|
||||||
elementObject.add("disabled", element.isDisabled());
|
elementConfig.setDisabled(element.isDisabled());
|
||||||
elementObject.add("x", element.getPosition().getX());
|
elementConfig.setX(element.getPosition().getX());
|
||||||
elementObject.add("y", element.getPosition().getY());
|
elementConfig.setY(element.getPosition().getY());
|
||||||
elementObject.add("z", element.getPosition().getZ());
|
elementConfig.setZ(element.getPosition().getZ());
|
||||||
element.saveExtra(elementObject);
|
element.saveExtra(elementConfig);
|
||||||
|
|
||||||
YAPIONArray phases = new YAPIONArray();
|
|
||||||
element.getPhases().forEach(phase -> {
|
element.getPhases().forEach(phase -> {
|
||||||
YAPIONObject phaseObject = new YAPIONObject();
|
SimulatorConfig.Phase phaseConfig = new SimulatorConfig.Phase();
|
||||||
phaseObject.add("tickOffset", phase.getTickOffset());
|
phaseConfig.setTickOffset(phase.getTickOffset());
|
||||||
phaseObject.add("lifetime", phase.getLifetime());
|
phaseConfig.setLifetime(phase.getLifetime());
|
||||||
phaseObject.add("order", phase.getOrder());
|
phaseConfig.setOrder(phase.getOrder());
|
||||||
phase.saveExtra(phaseObject);
|
phase.saveExtra(phaseConfig);
|
||||||
phases.add(phaseObject);
|
elementConfig.getPhases().add(phaseConfig);
|
||||||
});
|
});
|
||||||
elementObject.add("phases", phases);
|
groupConfig.getElements().add(elementConfig);
|
||||||
elements.add(elementObject);
|
|
||||||
});
|
});
|
||||||
groupObject.add("elements", elements);
|
config.getGroups().add(groupConfig);
|
||||||
groups.add(groupObject);
|
|
||||||
});
|
});
|
||||||
simulatorObject.add("groups", groups);
|
|
||||||
|
|
||||||
File file = new File(directory, simulator.getName() + ".sim");
|
File file = new File(directory, simulator.getName() + ".sim");
|
||||||
try {
|
try {
|
||||||
simulatorObject.toJSONLossy(new FileOutput(file)).close();
|
SimulatorConfig.STORE.save(file, config);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
// Ignore
|
// Ignore
|
||||||
}
|
}
|
||||||
|
|||||||
+31
-21
@@ -19,6 +19,7 @@
|
|||||||
|
|
||||||
package de.steamwar.bausystem.features.simulator.storage;
|
package de.steamwar.bausystem.features.simulator.storage;
|
||||||
|
|
||||||
|
import de.steamwar.bausystem.config.legacy.YapionReader;
|
||||||
import de.steamwar.bausystem.features.simulator.data.Simulator;
|
import de.steamwar.bausystem.features.simulator.data.Simulator;
|
||||||
import de.steamwar.bausystem.features.simulator.data.SimulatorGroup;
|
import de.steamwar.bausystem.features.simulator.data.SimulatorGroup;
|
||||||
import de.steamwar.bausystem.features.simulator.data.tnt.TNTElement;
|
import de.steamwar.bausystem.features.simulator.data.tnt.TNTElement;
|
||||||
@@ -26,18 +27,23 @@ import de.steamwar.bausystem.features.simulator.data.tnt.TNTPhase;
|
|||||||
import de.steamwar.sql.SteamwarUser;
|
import de.steamwar.sql.SteamwarUser;
|
||||||
import org.bukkit.Material;
|
import org.bukkit.Material;
|
||||||
import org.bukkit.util.Vector;
|
import org.bukkit.util.Vector;
|
||||||
import yapion.exceptions.YAPIONException;
|
|
||||||
import yapion.hierarchy.types.YAPIONArray;
|
|
||||||
import yapion.hierarchy.types.YAPIONObject;
|
|
||||||
import yapion.parser.YAPIONParser;
|
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.nio.file.Files;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
|
import static de.steamwar.bausystem.config.legacy.YapionReader.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads the legacy per player .yapion files containing multiple simulators, they are
|
||||||
|
* deleted after loading and saved in the current format by the {@link SimulatorSaver}.
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
public class YAPIONFormatSimulatorLoader implements SimulatorLoader {
|
public class YAPIONFormatSimulatorLoader implements SimulatorLoader {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -46,10 +52,10 @@ public class YAPIONFormatSimulatorLoader implements SimulatorLoader {
|
|||||||
return Optional.empty();
|
return Optional.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
YAPIONObject yapionObject;
|
Map<String, Object> yapionObject;
|
||||||
try {
|
try {
|
||||||
yapionObject = YAPIONParser.parse(file);
|
yapionObject = YapionReader.parse(Files.readString(file.toPath(), StandardCharsets.UTF_8));
|
||||||
} catch (YAPIONException | IOException e) {
|
} catch (IllegalArgumentException | IOException e) {
|
||||||
return Optional.empty();
|
return Optional.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -57,28 +63,28 @@ public class YAPIONFormatSimulatorLoader implements SimulatorLoader {
|
|||||||
SteamwarUser steamwarUser = SteamwarUser.byId(Integer.parseInt(name));
|
SteamwarUser steamwarUser = SteamwarUser.byId(Integer.parseInt(name));
|
||||||
|
|
||||||
List<Simulator> simulators = new ArrayList<>();
|
List<Simulator> simulators = new ArrayList<>();
|
||||||
for (String s : yapionObject.getKeys()) {
|
for (String s : yapionObject.keySet()) {
|
||||||
String newName = steamwarUser.getUserName() + (s.isEmpty() ? "" : "_" + s);
|
String newName = steamwarUser.getUserName() + (s.isEmpty() ? "" : "_" + s);
|
||||||
YAPIONArray content = yapionObject.getArray(s);
|
List<Map<?, ?>> content = getObjects(yapionObject, s);
|
||||||
if (content.isEmpty()) continue;
|
if (content.isEmpty()) continue;
|
||||||
Simulator simulator = new Simulator(newName);
|
Simulator simulator = new Simulator(newName);
|
||||||
for (YAPIONObject element : content.streamObject().collect(Collectors.toList())) {
|
for (Map<?, ?> element : content) {
|
||||||
SimulatorGroup simulatorGroup = new SimulatorGroup();
|
SimulatorGroup simulatorGroup = new SimulatorGroup();
|
||||||
simulator.add(simulatorGroup);
|
simulator.add(simulatorGroup);
|
||||||
|
|
||||||
TNTElement tntElement = new TNTElement(new Vector(element.getDouble("positionX"), element.getDouble("positionY"), element.getDouble("positionZ")));
|
TNTElement tntElement = new TNTElement(new Vector(requireDouble(element, "positionX"), requireDouble(element, "positionY"), requireDouble(element, "positionZ")));
|
||||||
tntElement.setDisabled(element.getBooleanOrDefault("disabled", false)); // IDK if this existed back then?
|
tntElement.setDisabled(getBoolean(element, "disabled", false)); // IDK if this existed back then?
|
||||||
tntElement.setMaterial(Material.valueOf(element.getStringOrDefault("material", "TNT")));
|
tntElement.setMaterial(Material.valueOf(getString(element, "material", "TNT")));
|
||||||
simulatorGroup.add(tntElement);
|
simulatorGroup.add(tntElement);
|
||||||
|
|
||||||
TNTPhase tntPhase = new TNTPhase();
|
TNTPhase tntPhase = new TNTPhase();
|
||||||
tntPhase.setLifetime(element.getIntOrDefault("fuseTicks", 80));
|
tntPhase.setLifetime(getInt(element, "fuseTicks", 80));
|
||||||
tntPhase.setCount(element.getIntOrDefault("count", 1));
|
tntPhase.setCount(getInt(element, "count", 1));
|
||||||
tntPhase.setTickOffset(element.getIntOrDefault("tickOffset", 0));
|
tntPhase.setTickOffset(getInt(element, "tickOffset", 0));
|
||||||
tntPhase.setXJump(element.getBooleanOrDefault("xVelocity", false));
|
tntPhase.setXJump(getBoolean(element, "xVelocity", false));
|
||||||
tntPhase.setYJump(element.getBooleanOrDefault("yVelocity", false));
|
tntPhase.setYJump(getBoolean(element, "yVelocity", false));
|
||||||
tntPhase.setZJump(element.getBooleanOrDefault("zVelocity", false));
|
tntPhase.setZJump(getBoolean(element, "zVelocity", false));
|
||||||
tntPhase.setOrder(element.getBooleanOrDefault("comparator", false) ? 2 : 0);
|
tntPhase.setOrder(getBoolean(element, "comparator", false) ? 2 : 0);
|
||||||
tntElement.add(tntPhase);
|
tntElement.add(tntPhase);
|
||||||
}
|
}
|
||||||
simulators.add(simulator);
|
simulators.add(simulator);
|
||||||
@@ -87,4 +93,8 @@ public class YAPIONFormatSimulatorLoader implements SimulatorLoader {
|
|||||||
file.delete();
|
file.delete();
|
||||||
return Optional.of(simulators);
|
return Optional.of(simulators);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private double requireDouble(Map<?, ?> element, String key) {
|
||||||
|
return ((Number) element.get(key)).doubleValue();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+64
@@ -0,0 +1,64 @@
|
|||||||
|
/*
|
||||||
|
* This file is a part of the SteamWar software.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2026 SteamWar.de-Serverteam
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program 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 Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.steamwar.bausystem.features.slaves.laufbau;
|
||||||
|
|
||||||
|
import de.steamwar.bausystem.config.ConfigStore;
|
||||||
|
import de.steamwar.bausystem.config.VersionedConfig;
|
||||||
|
import de.steamwar.bausystem.features.slaves.laufbau.converter.LaufbauConfigV1ToV2Converter;
|
||||||
|
import lombok.AccessLevel;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The current laufbau config of a player. It is stored as JSON, fields missing
|
||||||
|
* in the stored JSON keep the default value of their initializer, so adding a
|
||||||
|
* new field does not need a new version.
|
||||||
|
* <p>
|
||||||
|
* When a field gets removed, renamed or its meaning changes, copy this class
|
||||||
|
* to LaufbauConfigV{@link #VERSION} (without the {@link #STORE}), increase {@link #VERSION}, write a
|
||||||
|
* {@link de.steamwar.bausystem.config.ConfigConverter} from the copy to
|
||||||
|
* this class and register it in the {@link #STORE}.
|
||||||
|
*/
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
public class LaufbauConfig implements VersionedConfig {
|
||||||
|
|
||||||
|
public static final int VERSION = 2;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* LaufbauConfigV1 is stored as Base64 of gzipped YAPION, the current version as JSON
|
||||||
|
*/
|
||||||
|
public static final ConfigStore<LaufbauConfig> STORE = new ConfigStore<>(LaufbauConfig.class, VERSION, LaufbauConfig::new)
|
||||||
|
.legacy(s -> !s.startsWith("{"), LaufbauConfigV1::parse)
|
||||||
|
.converter(new LaufbauConfigV1ToV2Converter())
|
||||||
|
.userConfig("bausystem-laufbau")
|
||||||
|
.removeIf(config -> config.getDeactivated().isEmpty());
|
||||||
|
|
||||||
|
@Setter(AccessLevel.NONE)
|
||||||
|
protected int version = VERSION;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Identifiers of the deactivated {@link BlockBoundingBox}es, see {@link LaufbauUtils#identifier(BlockBoundingBox)}
|
||||||
|
*/
|
||||||
|
protected Set<String> deactivated = new HashSet<>();
|
||||||
|
}
|
||||||
+65
@@ -0,0 +1,65 @@
|
|||||||
|
/*
|
||||||
|
* This file is a part of the SteamWar software.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2026 SteamWar.de-Serverteam
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program 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 Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.steamwar.bausystem.features.slaves.laufbau;
|
||||||
|
|
||||||
|
import de.steamwar.bausystem.config.VersionedConfig;
|
||||||
|
import de.steamwar.bausystem.config.legacy.YapionReader;
|
||||||
|
import lombok.AccessLevel;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.UncheckedIOException;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.Base64;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.zip.GZIPInputStream;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The legacy laufbau config that was stored as Base64 of the gzipped YAPION,
|
||||||
|
* an object with the deactivated identifiers as keys.
|
||||||
|
*/
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
@Deprecated
|
||||||
|
public class LaufbauConfigV1 implements VersionedConfig {
|
||||||
|
|
||||||
|
public static final int VERSION = 1;
|
||||||
|
|
||||||
|
@Setter(AccessLevel.NONE)
|
||||||
|
protected int version = VERSION;
|
||||||
|
|
||||||
|
protected Set<String> deactivated = new HashSet<>();
|
||||||
|
|
||||||
|
public static LaufbauConfigV1 parse(String base64) {
|
||||||
|
String yapion;
|
||||||
|
try (InputStream inputStream = new GZIPInputStream(new ByteArrayInputStream(Base64.getDecoder().decode(base64)))) {
|
||||||
|
yapion = new String(inputStream.readAllBytes(), StandardCharsets.UTF_8);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new UncheckedIOException(e);
|
||||||
|
}
|
||||||
|
LaufbauConfigV1 config = new LaufbauConfigV1();
|
||||||
|
config.deactivated.addAll(YapionReader.parse(yapion).keySet());
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
}
|
||||||
+5
-48
@@ -20,63 +20,22 @@
|
|||||||
package de.steamwar.bausystem.features.slaves.laufbau;
|
package de.steamwar.bausystem.features.slaves.laufbau;
|
||||||
|
|
||||||
import de.steamwar.bausystem.BauSystem;
|
import de.steamwar.bausystem.BauSystem;
|
||||||
import de.steamwar.core.SWPlayer;
|
|
||||||
import de.steamwar.inventory.SWItem;
|
import de.steamwar.inventory.SWItem;
|
||||||
import de.steamwar.sql.UserConfig;
|
|
||||||
import lombok.Cleanup;
|
|
||||||
import lombok.SneakyThrows;
|
import lombok.SneakyThrows;
|
||||||
import lombok.experimental.UtilityClass;
|
import lombok.experimental.UtilityClass;
|
||||||
import org.bukkit.Material;
|
import org.bukkit.Material;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
import org.bukkit.inventory.meta.ItemMeta;
|
import org.bukkit.inventory.meta.ItemMeta;
|
||||||
import yapion.hierarchy.output.StreamOutput;
|
|
||||||
import yapion.hierarchy.types.YAPIONObject;
|
|
||||||
import yapion.parser.YAPIONParser;
|
|
||||||
import yapion.parser.options.StreamOptions;
|
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
|
||||||
import java.io.ByteArrayOutputStream;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Base64;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import java.util.zip.GZIPInputStream;
|
|
||||||
import java.util.zip.GZIPOutputStream;
|
|
||||||
|
|
||||||
@UtilityClass
|
@UtilityClass
|
||||||
public class LaufbauUtils {
|
public class LaufbauUtils {
|
||||||
|
|
||||||
public static class LaufbauComponent implements SWPlayer.Component {
|
|
||||||
|
|
||||||
private YAPIONObject yapionObject = new YAPIONObject();
|
|
||||||
|
|
||||||
@Override
|
|
||||||
@SneakyThrows
|
|
||||||
public void onMount(SWPlayer player) {
|
|
||||||
String config = UserConfig.getConfig(player.getPlayer().getUniqueId(), "bausystem-laufbau");
|
|
||||||
if (config == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
byte[] bytes = Base64.getDecoder().decode(config);
|
|
||||||
@Cleanup GZIPInputStream gzipInputStream = new GZIPInputStream(new ByteArrayInputStream(bytes));
|
|
||||||
yapionObject = new YAPIONParser(gzipInputStream, new StreamOptions().stopOnStreamEnd(true)).parse().result();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
@SneakyThrows
|
|
||||||
public void onUnmount(SWPlayer player) {
|
|
||||||
if (yapionObject.isEmpty()) {
|
|
||||||
UserConfig.removePlayerConfig(player.getPlayer().getUniqueId(), "bausystem-laufbau");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
@Cleanup ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
|
|
||||||
@Cleanup GZIPOutputStream gzipOutputStream = new GZIPOutputStream(byteArrayOutputStream);
|
|
||||||
yapionObject.toYAPION(new StreamOutput(gzipOutputStream)).close();
|
|
||||||
UserConfig.updatePlayerConfig(player.getPlayer().getUniqueId(), "bausystem-laufbau", Base64.getEncoder().encodeToString(byteArrayOutputStream.toByteArray()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Cuboid pixelCuboid(double pixelX, double pixelY, double pixelZ, double pixelDX, double pixelDY, double pixelDZ) {
|
public static Cuboid pixelCuboid(double pixelX, double pixelY, double pixelZ, double pixelDX, double pixelDY, double pixelDZ) {
|
||||||
return new Cuboid(pixelToBlock(pixelX), pixelToBlock(pixelY), pixelToBlock(pixelZ), pixelToBlock(pixelDX), pixelToBlock(pixelDY), pixelToBlock(pixelDZ));
|
return new Cuboid(pixelToBlock(pixelX), pixelToBlock(pixelY), pixelToBlock(pixelZ), pixelToBlock(pixelDX), pixelToBlock(pixelDY), pixelToBlock(pixelDZ));
|
||||||
}
|
}
|
||||||
@@ -123,7 +82,7 @@ public class LaufbauUtils {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
String identifier = identifier(bb);
|
String identifier = identifier(bb);
|
||||||
return SWPlayer.of(p).getComponentOrDefault(LaufbauComponent.class, LaufbauComponent::new).yapionObject.containsKey(identifier);
|
return LaufbauConfig.STORE.get(p).getDeactivated().contains(identifier);
|
||||||
}
|
}
|
||||||
|
|
||||||
@SneakyThrows
|
@SneakyThrows
|
||||||
@@ -132,11 +91,9 @@ public class LaufbauUtils {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
String identifier = identifier(bb);
|
String identifier = identifier(bb);
|
||||||
YAPIONObject yapionObject = SWPlayer.of(p).getComponentOrDefault(LaufbauComponent.class, LaufbauComponent::new).yapionObject;
|
Set<String> deactivated = LaufbauConfig.STORE.get(p).getDeactivated();
|
||||||
if (yapionObject.containsKey(identifier)) {
|
if (!deactivated.remove(identifier)) {
|
||||||
yapionObject.remove(identifier);
|
deactivated.add(identifier);
|
||||||
} else {
|
|
||||||
yapionObject.put(identifier, new YAPIONObject());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+40
@@ -0,0 +1,40 @@
|
|||||||
|
/*
|
||||||
|
* This file is a part of the SteamWar software.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2026 SteamWar.de-Serverteam
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program 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 Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.steamwar.bausystem.features.slaves.laufbau.converter;
|
||||||
|
|
||||||
|
import de.steamwar.bausystem.config.ConfigConverter;
|
||||||
|
import de.steamwar.bausystem.features.slaves.laufbau.LaufbauConfig;
|
||||||
|
import de.steamwar.bausystem.features.slaves.laufbau.LaufbauConfigV1;
|
||||||
|
|
||||||
|
@Deprecated
|
||||||
|
public class LaufbauConfigV1ToV2Converter implements ConfigConverter<LaufbauConfigV1, LaufbauConfig> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int version() {
|
||||||
|
return LaufbauConfigV1.VERSION;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public LaufbauConfig convert(LaufbauConfigV1 old) {
|
||||||
|
LaufbauConfig config = new LaufbauConfig();
|
||||||
|
config.getDeactivated().addAll(old.getDeactivated());
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
}
|
||||||
+2
-2
@@ -35,8 +35,8 @@ public class SmartPlaceCommand extends SWCommand {
|
|||||||
|
|
||||||
@Register(description = {"SMART_PLACE_HELP", "SMART_PLACE_INFO"})
|
@Register(description = {"SMART_PLACE_HELP", "SMART_PLACE_INFO"})
|
||||||
public void genericToggle(Player p) {
|
public void genericToggle(Player p) {
|
||||||
boolean smartPlace = Config.getInstance().get(p).getPlainValueOrDefault("smartPlace", false);
|
boolean smartPlace = Config.STORE.get(p).isSmartPlace();
|
||||||
Config.getInstance().get(p).put("smartPlace", !smartPlace);
|
Config.STORE.get(p).setSmartPlace(!smartPlace);
|
||||||
if (!smartPlace) {
|
if (!smartPlace) {
|
||||||
SWUtils.sendToActionbar(p, BauSystem.MESSAGE.parse("SMART_PLACE_ENABLE", p));
|
SWUtils.sendToActionbar(p, BauSystem.MESSAGE.parse("SMART_PLACE_ENABLE", p));
|
||||||
BauSystem.MESSAGE.send("SMART_PLACE_INFO", p);
|
BauSystem.MESSAGE.send("SMART_PLACE_INFO", p);
|
||||||
|
|||||||
+6
-3
@@ -25,6 +25,7 @@ import de.steamwar.bausystem.Permission;
|
|||||||
import de.steamwar.bausystem.configplayer.Config;
|
import de.steamwar.bausystem.configplayer.Config;
|
||||||
import de.steamwar.linkage.Linked;
|
import de.steamwar.linkage.Linked;
|
||||||
import net.minecraft.network.protocol.game.ServerboundUseItemOnPacket;
|
import net.minecraft.network.protocol.game.ServerboundUseItemOnPacket;
|
||||||
|
import net.minecraft.world.InteractionHand;
|
||||||
import org.bukkit.*;
|
import org.bukkit.*;
|
||||||
import org.bukkit.block.Block;
|
import org.bukkit.block.Block;
|
||||||
import org.bukkit.block.BlockFace;
|
import org.bukkit.block.BlockFace;
|
||||||
@@ -87,7 +88,9 @@ public class SmartPlaceListener implements Listener {
|
|||||||
public SmartPlaceListener() {
|
public SmartPlaceListener() {
|
||||||
TinyProtocol.instance.addFilter(ServerboundUseItemOnPacket.class, (player, packet) -> {
|
TinyProtocol.instance.addFilter(ServerboundUseItemOnPacket.class, (player, packet) -> {
|
||||||
if (!Permission.BUILD.hasPermission(player)) return packet;
|
if (!Permission.BUILD.hasPermission(player)) return packet;
|
||||||
if (!Config.getInstance().get(player).getPlainValueOrDefault("smartPlace", false)) return packet;
|
if (!Config.STORE.get(player).isSmartPlace()) return packet;
|
||||||
|
ItemStack usedItem = packet.getHand() == InteractionHand.MAIN_HAND ? player.getInventory().getItemInMainHand() : player.getInventory().getItemInOffHand();
|
||||||
|
if (usedItem.getType() == Material.DEBUG_STICK) return packet;
|
||||||
RayTraceResult rayTraceResult = player.rayTraceBlocks(6);
|
RayTraceResult rayTraceResult = player.rayTraceBlocks(6);
|
||||||
Block block = rayTraceResult != null ? rayTraceResult.getHitBlock() : null;
|
Block block = rayTraceResult != null ? rayTraceResult.getHitBlock() : null;
|
||||||
BlockFace blockFace = rayTraceResult != null ? rayTraceResult.getHitBlockFace() : null;
|
BlockFace blockFace = rayTraceResult != null ? rayTraceResult.getHitBlockFace() : null;
|
||||||
@@ -127,7 +130,7 @@ public class SmartPlaceListener implements Listener {
|
|||||||
@EventHandler
|
@EventHandler
|
||||||
public void onPlayerInteract(PlayerInteractEvent event) {
|
public void onPlayerInteract(PlayerInteractEvent event) {
|
||||||
if (!Permission.BUILD.hasPermission(event.getPlayer())) return;
|
if (!Permission.BUILD.hasPermission(event.getPlayer())) return;
|
||||||
if (!Config.getInstance().get(event.getPlayer()).getPlainValueOrDefault("smartPlace", false)) return;
|
if (!Config.STORE.get(event.getPlayer()).isSmartPlace()) return;
|
||||||
WAS_EXECUTED.add(event.getPlayer());
|
WAS_EXECUTED.add(event.getPlayer());
|
||||||
if (event.getAction() != Action.RIGHT_CLICK_BLOCK) return;
|
if (event.getAction() != Action.RIGHT_CLICK_BLOCK) return;
|
||||||
if (event.getPlayer().getGameMode() == GameMode.SPECTATOR) return;
|
if (event.getPlayer().getGameMode() == GameMode.SPECTATOR) return;
|
||||||
@@ -146,7 +149,7 @@ public class SmartPlaceListener implements Listener {
|
|||||||
@EventHandler
|
@EventHandler
|
||||||
public void onBlockPlace(BlockPlaceEvent event) {
|
public void onBlockPlace(BlockPlaceEvent event) {
|
||||||
if (!Permission.BUILD.hasPermission(event.getPlayer())) return;
|
if (!Permission.BUILD.hasPermission(event.getPlayer())) return;
|
||||||
if (!Config.getInstance().get(event.getPlayer()).getPlainValueOrDefault("smartPlace", false)) return;
|
if (!Config.STORE.get(event.getPlayer()).isSmartPlace()) return;
|
||||||
if (!SMART_PLACING.contains(event.getPlayer())) {
|
if (!SMART_PLACING.contains(event.getPlayer())) {
|
||||||
if (CONTAINERS.contains(event.getBlockAgainst().getType())) {
|
if (CONTAINERS.contains(event.getBlockAgainst().getType())) {
|
||||||
SoundGroup soundGroup = event.getBlockPlaced().getBlockData().getSoundGroup();
|
SoundGroup soundGroup = event.getBlockPlaced().getBlockData().getSoundGroup();
|
||||||
|
|||||||
+3
-6
@@ -35,18 +35,15 @@ public class BlockCounter {
|
|||||||
public final Map<Region, BlockCount> blockCountMap = new HashMap<>();
|
public final Map<Region, BlockCount> blockCountMap = new HashMap<>();
|
||||||
|
|
||||||
public boolean isActive(final Player p) {
|
public boolean isActive(final Player p) {
|
||||||
if (!Config.getInstance().get(p).containsKey("blockCounter")) {
|
return Config.STORE.get(p).isBlockCounter();
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return Config.getInstance().get(p).getPlainValue("blockCounter");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void enable(final Player p) {
|
public void enable(final Player p) {
|
||||||
Config.getInstance().get(p).put("blockCounter", true);
|
Config.STORE.get(p).setBlockCounter(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void disable(final Player p) {
|
public void disable(final Player p) {
|
||||||
Config.getInstance().get(p).put("blockCounter", false);
|
Config.STORE.get(p).setBlockCounter(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getMessage(Player player, int count, int tntCount, long tick, long lastTick) {
|
public String getMessage(Player player, int count, int tntCount, long tick, long lastTick) {
|
||||||
|
|||||||
+1
-4
@@ -36,13 +36,10 @@ import org.bukkit.entity.Player;
|
|||||||
import org.bukkit.util.Transformation;
|
import org.bukkit.util.Transformation;
|
||||||
import org.joml.Quaternionf;
|
import org.joml.Quaternionf;
|
||||||
import org.joml.Vector3f;
|
import org.joml.Vector3f;
|
||||||
import yapion.hierarchy.types.YAPIONValue;
|
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import static de.steamwar.bausystem.features.util.TNTClickListener.TNT_CLICK_DETAILS;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wrapper for the rendering of a record bundle
|
* Wrapper for the rendering of a record bundle
|
||||||
*/
|
*/
|
||||||
@@ -102,7 +99,7 @@ public class TraceEntity extends RBlockDisplay {
|
|||||||
* @param player the player the message should be printed for
|
* @param player the player the message should be printed for
|
||||||
*/
|
*/
|
||||||
public void printIntoChat(Player player) {
|
public void printIntoChat(Player player) {
|
||||||
if (!Config.getInstance().get(player).getOrSetDefault(TNT_CLICK_DETAILS, new YAPIONValue<>(true)).asBoolean().orElse(true)) {
|
if (!Config.STORE.get(player).isTntClickDetails()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
TNTPoint representative = records.get(0);
|
TNTPoint representative = records.get(0);
|
||||||
|
|||||||
+4
-5
@@ -30,7 +30,6 @@ import org.bukkit.event.Listener;
|
|||||||
import org.bukkit.event.player.PlayerJoinEvent;
|
import org.bukkit.event.player.PlayerJoinEvent;
|
||||||
import org.bukkit.potion.PotionEffect;
|
import org.bukkit.potion.PotionEffect;
|
||||||
import org.bukkit.potion.PotionEffectType;
|
import org.bukkit.potion.PotionEffectType;
|
||||||
import yapion.hierarchy.types.YAPIONObject;
|
|
||||||
|
|
||||||
@Linked
|
@Linked
|
||||||
public class NightVisionCommand extends SWCommand implements Listener {
|
public class NightVisionCommand extends SWCommand implements Listener {
|
||||||
@@ -41,15 +40,15 @@ public class NightVisionCommand extends SWCommand implements Listener {
|
|||||||
|
|
||||||
@Register(description = "NIGHT_VISION_HELP")
|
@Register(description = "NIGHT_VISION_HELP")
|
||||||
public void genericCommand(Player p) {
|
public void genericCommand(Player p) {
|
||||||
YAPIONObject yapionObject = Config.getInstance().get(p);
|
Config config = Config.STORE.get(p);
|
||||||
boolean value = !yapionObject.getBooleanOrDefault("nightvision", false);
|
boolean value = !config.isNightVision();
|
||||||
yapionObject.put("nightvision", value);
|
config.setNightVision(value);
|
||||||
setNightVision(p, value);
|
setNightVision(p, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
@EventHandler
|
@EventHandler
|
||||||
public void onPlayerJoin(PlayerJoinEvent event) {
|
public void onPlayerJoin(PlayerJoinEvent event) {
|
||||||
setNightVision(event.getPlayer(), Config.getInstance().get(event.getPlayer()).getBooleanOrDefault("nightvision", false));
|
setNightVision(event.getPlayer(), Config.STORE.get(event.getPlayer()).isNightVision());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setNightVision(Player p, boolean value) {
|
private void setNightVision(Player p, boolean value) {
|
||||||
|
|||||||
+30
-88
@@ -22,122 +22,71 @@ package de.steamwar.bausystem.features.util;
|
|||||||
import com.comphenix.tinyprotocol.TinyProtocol;
|
import com.comphenix.tinyprotocol.TinyProtocol;
|
||||||
import com.mojang.authlib.GameProfile;
|
import com.mojang.authlib.GameProfile;
|
||||||
import de.steamwar.bausystem.BauSystem;
|
import de.steamwar.bausystem.BauSystem;
|
||||||
import de.steamwar.bausystem.features.tpslimit.TPSUtils;
|
|
||||||
import de.steamwar.bausystem.utils.BauMemberUpdateEvent;
|
import de.steamwar.bausystem.utils.BauMemberUpdateEvent;
|
||||||
import de.steamwar.command.SWCommand;
|
import de.steamwar.command.SWCommand;
|
||||||
import de.steamwar.core.ProtocolWrapper;
|
import de.steamwar.core.ProtocolWrapper;
|
||||||
import de.steamwar.core.SWPlayer;
|
import de.steamwar.core.SWPlayer;
|
||||||
import de.steamwar.linkage.Linked;
|
import de.steamwar.linkage.Linked;
|
||||||
import net.minecraft.network.protocol.game.*;
|
import io.papermc.paper.event.player.PlayerFailMoveEvent;
|
||||||
import net.minecraft.world.entity.player.Abilities;
|
import net.minecraft.network.protocol.game.ClientboundGameEventPacket;
|
||||||
import net.minecraft.world.level.GameType;
|
|
||||||
import org.bukkit.Bukkit;
|
|
||||||
import org.bukkit.GameMode;
|
import org.bukkit.GameMode;
|
||||||
import org.bukkit.craftbukkit.entity.CraftPlayer;
|
|
||||||
import org.bukkit.craftbukkit.inventory.CraftItemStack;
|
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
import org.bukkit.event.EventHandler;
|
import org.bukkit.event.EventHandler;
|
||||||
import org.bukkit.event.Listener;
|
import org.bukkit.event.Listener;
|
||||||
import org.bukkit.event.block.BlockCanBuildEvent;
|
import org.bukkit.event.block.BlockCanBuildEvent;
|
||||||
import org.bukkit.event.player.PlayerGameModeChangeEvent;
|
import org.bukkit.event.player.PlayerGameModeChangeEvent;
|
||||||
import org.bukkit.event.player.PlayerTeleportEvent;
|
|
||||||
import org.bukkit.event.player.PlayerToggleFlightEvent;
|
import org.bukkit.event.player.PlayerToggleFlightEvent;
|
||||||
|
|
||||||
import java.util.function.BiFunction;
|
|
||||||
|
|
||||||
@Linked
|
@Linked
|
||||||
public class NoClipCommand extends SWCommand implements Listener {
|
public class NoClipCommand extends SWCommand implements Listener {
|
||||||
|
|
||||||
public static class NoClipData implements SWPlayer.Component {
|
public static class NoClipData implements SWPlayer.Component {
|
||||||
private long lastTick = -1;
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onUnmount(SWPlayer player) {
|
public void onUnmount(SWPlayer swPlayer) {
|
||||||
player.setGameMode(GameMode.CREATIVE);
|
Player player = swPlayer.getPlayer();
|
||||||
|
showClientGameMode(player, GameMode.CREATIVE, GameMode.CREATIVE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public NoClipCommand() {
|
public NoClipCommand() {
|
||||||
super("noclip", "nc");
|
super("noclip", "nc");
|
||||||
|
|
||||||
BiFunction<Player, ServerboundMovePlayerPacket, Object> first = (player, o) -> {
|
|
||||||
NoClipData noClipData = SWPlayer.of(player).getComponent(NoClipData.class).orElse(null);
|
|
||||||
if (noClipData == null) return o;
|
|
||||||
if (noClipData.lastTick == TPSUtils.currentTick.get()) return o;
|
|
||||||
setInternalGameMode(player, GameMode.SPECTATOR);
|
|
||||||
noClipData.lastTick = TPSUtils.currentTick.get();
|
|
||||||
return o;
|
|
||||||
};
|
|
||||||
TinyProtocol.instance.addFilter(ServerboundMovePlayerPacket.Pos.class, first);
|
|
||||||
TinyProtocol.instance.addFilter(ServerboundMovePlayerPacket.PosRot.class, first);
|
|
||||||
|
|
||||||
BiFunction<Player, Object, Object> second = (player, o) -> {
|
|
||||||
NoClipData noClipData = SWPlayer.of(player).getComponent(NoClipData.class).orElse(null);
|
|
||||||
if (noClipData == null) return o;
|
|
||||||
setInternalGameMode(player, GameMode.CREATIVE);
|
|
||||||
noClipData.lastTick = TPSUtils.currentTick.get();
|
|
||||||
return o;
|
|
||||||
};
|
|
||||||
TinyProtocol.instance.addFilter(ServerboundUseItemOnPacket.class, second);
|
|
||||||
TinyProtocol.instance.addFilter(ServerboundPlayerActionPacket.class, second);
|
|
||||||
TinyProtocol.instance.addFilter(ServerboundContainerClickPacket.class, second);
|
|
||||||
|
|
||||||
BiFunction<Player, ServerboundSetCreativeModeSlotPacket, Object> third = (player, o) -> {
|
|
||||||
if (SWPlayer.of(player).hasComponent(NoClipData.class)) {
|
|
||||||
int index = o.slotNum();
|
|
||||||
if (index >= 36 && index <= 44) {
|
|
||||||
index -= 36;
|
|
||||||
} else if (index > 44) {
|
|
||||||
index -= 5;
|
|
||||||
} else if (index <= 8) {
|
|
||||||
index = index - 8 + 36;
|
|
||||||
}
|
|
||||||
player.getInventory().setItem(index, CraftItemStack.asBukkitCopy(o.itemStack()));
|
|
||||||
if (index < 9) player.getInventory().setHeldItemSlot(index);
|
|
||||||
player.updateInventory();
|
|
||||||
}
|
|
||||||
return o;
|
|
||||||
};
|
|
||||||
TinyProtocol.instance.addFilter(ServerboundSetCreativeModeSlotPacket.class, third);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setInternalGameMode(Player player, GameMode gameMode) {
|
@Register
|
||||||
((CraftPlayer) player).getHandle().gameMode.gameModeForPlayer = GameType.byId(gameMode.getValue());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Register(help = true)
|
|
||||||
public void genericCommand(@Validator Player player) {
|
public void genericCommand(@Validator Player player) {
|
||||||
SWPlayer swPlayer = SWPlayer.of(player);
|
SWPlayer swPlayer = SWPlayer.of(player);
|
||||||
if (swPlayer.hasComponent(NoClipData.class)) {
|
if (swPlayer.hasComponent(NoClipData.class)) {
|
||||||
swPlayer.removeComponent(NoClipData.class);
|
swPlayer.removeComponent(NoClipData.class);
|
||||||
} else {
|
} else {
|
||||||
player.setGameMode(GameMode.SPECTATOR);
|
player.setGameMode(GameMode.CREATIVE);
|
||||||
Abilities abilities = (((CraftPlayer) player).getHandle()).getAbilities();
|
player.setAllowFlight(true);
|
||||||
abilities.mayBuild = true;
|
player.setFlying(true);
|
||||||
abilities.mayfly = true;
|
|
||||||
|
|
||||||
swPlayer.setComponent(new NoClipData());
|
swPlayer.setComponent(new NoClipData());
|
||||||
BauSystem.MESSAGE.send("OTHER_NOCLIP_SLOT_INFO", player);
|
BauSystem.MESSAGE.send("OTHER_NOCLIP_SLOT_INFO", player);
|
||||||
TinyProtocol.instance.sendPacket(player, new ClientboundGameEventPacket(ClientboundGameEventPacket.CHANGE_GAME_MODE, 1F));
|
showClientGameMode(player, GameMode.CREATIVE, GameMode.SPECTATOR);
|
||||||
pseudoGameMode(player, GameMode.SPECTATOR);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@EventHandler
|
@EventHandler
|
||||||
public void onBauMemberUpdate(BauMemberUpdateEvent event) {
|
public void onPlayerFailMove(PlayerFailMoveEvent event) {
|
||||||
event.getNewSpectator().forEach(player -> {
|
if (event.getFailReason() != PlayerFailMoveEvent.FailReason.CLIPPED_INTO_BLOCK) return;
|
||||||
SWPlayer.of(player).removeComponent(NoClipData.class);
|
if (!SWPlayer.of(event.getPlayer()).hasComponent(NoClipData.class)) return;
|
||||||
});
|
event.setAllowed(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@EventHandler(ignoreCancelled = true)
|
@EventHandler
|
||||||
|
public void onBauMemberUpdate(BauMemberUpdateEvent event) {
|
||||||
|
event.getNewSpectator().forEach(player -> SWPlayer.of(player).removeComponent(NoClipData.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventHandler
|
||||||
public void onPlayerGameModeChange(PlayerGameModeChangeEvent event) {
|
public void onPlayerGameModeChange(PlayerGameModeChangeEvent event) {
|
||||||
if (SWPlayer.of(event.getPlayer()).hasComponent(NoClipData.class)) {
|
if (SWPlayer.of(event.getPlayer()).hasComponent(NoClipData.class)) {
|
||||||
event.setCancelled(true);
|
event.setCancelled(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@EventHandler(ignoreCancelled = true)
|
@EventHandler
|
||||||
public void onBlock(BlockCanBuildEvent event) {
|
public void onBlock(BlockCanBuildEvent event) {
|
||||||
if (event.getPlayer() == null) return;
|
if (event.getPlayer() == null) return;
|
||||||
if (SWPlayer.of(event.getPlayer()).hasComponent(NoClipData.class)) {
|
if (SWPlayer.of(event.getPlayer()).hasComponent(NoClipData.class)) {
|
||||||
@@ -145,7 +94,7 @@ public class NoClipCommand extends SWCommand implements Listener {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@EventHandler(ignoreCancelled = true)
|
@EventHandler
|
||||||
public void onPlayerToggleFlight(PlayerToggleFlightEvent event) {
|
public void onPlayerToggleFlight(PlayerToggleFlightEvent event) {
|
||||||
if (SWPlayer.of(event.getPlayer()).hasComponent(NoClipData.class)) {
|
if (SWPlayer.of(event.getPlayer()).hasComponent(NoClipData.class)) {
|
||||||
event.setCancelled(true);
|
event.setCancelled(true);
|
||||||
@@ -153,20 +102,13 @@ public class NoClipCommand extends SWCommand implements Listener {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@EventHandler(ignoreCancelled = true)
|
/**
|
||||||
public void onPlayerTeleport(PlayerTeleportEvent event) {
|
* @param player player
|
||||||
if (event.getCause() != PlayerTeleportEvent.TeleportCause.SPECTATE) {
|
* @param localMode GameMode for breaking and placing
|
||||||
return;
|
* @param listedMode GameMode for noPhysics
|
||||||
}
|
*/
|
||||||
if (SWPlayer.of(event.getPlayer()).hasComponent(NoClipData.class)) {
|
private static void showClientGameMode(Player player, GameMode localMode, GameMode listedMode) {
|
||||||
event.setCancelled(true);
|
TinyProtocol.instance.sendPacket(player, new ClientboundGameEventPacket(ClientboundGameEventPacket.CHANGE_GAME_MODE, localMode.getValue()));
|
||||||
Bukkit.getScheduler().runTaskLater(BauSystem.getInstance(), () -> {
|
TinyProtocol.instance.sendPacket(player, ProtocolWrapper.playerInfoPacketConstructor(ProtocolWrapper.PlayerInfoAction.GAMEMODE, new GameProfile(player.getUniqueId(), player.getName()), listedMode));
|
||||||
event.getPlayer().setSpectatorTarget(null);
|
|
||||||
}, 1L);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void pseudoGameMode(Player player, GameMode gameMode) {
|
|
||||||
TinyProtocol.instance.sendPacket(player, ProtocolWrapper.playerInfoPacketConstructor(ProtocolWrapper.PlayerInfoAction.GAMEMODE, new GameProfile(player.getUniqueId(), player.getName()), gameMode));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+57
-141
@@ -23,15 +23,17 @@ import de.steamwar.bausystem.BauSystem;
|
|||||||
import de.steamwar.bausystem.Permission;
|
import de.steamwar.bausystem.Permission;
|
||||||
import de.steamwar.entity.*;
|
import de.steamwar.entity.*;
|
||||||
import de.steamwar.linkage.Linked;
|
import de.steamwar.linkage.Linked;
|
||||||
|
import net.minecraft.core.BlockPos;
|
||||||
|
import net.minecraft.core.Direction;
|
||||||
|
import net.minecraft.world.level.block.piston.PistonStructureResolver;
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.Location;
|
import org.bukkit.Location;
|
||||||
import org.bukkit.Material;
|
import org.bukkit.Material;
|
||||||
import org.bukkit.block.Block;
|
import org.bukkit.block.Block;
|
||||||
import org.bukkit.block.BlockFace;
|
import org.bukkit.block.BlockFace;
|
||||||
import org.bukkit.block.PistonMoveReaction;
|
|
||||||
import org.bukkit.block.TileState;
|
|
||||||
import org.bukkit.block.data.BlockData;
|
import org.bukkit.block.data.BlockData;
|
||||||
import org.bukkit.block.data.type.Piston;
|
import org.bukkit.block.data.type.Piston;
|
||||||
|
import org.bukkit.craftbukkit.CraftWorld;
|
||||||
import org.bukkit.entity.Display;
|
import org.bukkit.entity.Display;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
import org.bukkit.entity.TextDisplay;
|
import org.bukkit.entity.TextDisplay;
|
||||||
@@ -92,41 +94,48 @@ public class PistonCalculator implements Listener {
|
|||||||
@EventHandler
|
@EventHandler
|
||||||
public void onBlockPistonExtend(BlockPistonExtendEvent event) {
|
public void onBlockPistonExtend(BlockPistonExtendEvent event) {
|
||||||
if (pistOrders.isEmpty()) return;
|
if (pistOrders.isEmpty()) return;
|
||||||
Bukkit.getScheduler().runTaskLater(BauSystem.getInstance(), () -> {
|
removeMovedPistOrders(event.getBlocks());
|
||||||
movePistOrders(event.getDirection(), event.getBlocks());
|
scheduleRecalculate();
|
||||||
pistOrders.values().forEach(PistOrder::calculate);
|
|
||||||
}, 3);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@EventHandler
|
@EventHandler
|
||||||
public void onBlockPistonRetract(BlockPistonRetractEvent event) {
|
public void onBlockPistonRetract(BlockPistonRetractEvent event) {
|
||||||
if (pistOrders.isEmpty()) return;
|
if (pistOrders.isEmpty()) return;
|
||||||
Bukkit.getScheduler().runTaskLater(BauSystem.getInstance(), () -> {
|
removeMovedPistOrders(event.getBlocks());
|
||||||
movePistOrders(event.getDirection(), event.getBlocks());
|
scheduleRecalculate();
|
||||||
pistOrders.values().forEach(PistOrder::calculate);
|
|
||||||
}, 3);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void movePistOrders(BlockFace direction, List<Block> blocks) {
|
private void removeMovedPistOrders(List<Block> blocks) {
|
||||||
Set<PistOrder> orders = new HashSet<>();
|
|
||||||
blocks.forEach(block -> {
|
blocks.forEach(block -> {
|
||||||
PistOrder pistOrder = pistOrders.get(block.getLocation());
|
PistOrder pistOrder = pistOrders.remove(block.getLocation());
|
||||||
if (pistOrder == null) return;
|
if (pistOrder != null) pistOrder.server.close();
|
||||||
pistOrders.remove(block.getLocation());
|
|
||||||
pistOrder.piston = pistOrder.piston.getRelative(direction);
|
|
||||||
orders.add(pistOrder);
|
|
||||||
});
|
|
||||||
orders.forEach(pistOrder -> {
|
|
||||||
pistOrders.put(pistOrder.piston.getLocation(), pistOrder);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static final long RECALCULATE_INTERVAL_MS = 200;
|
||||||
|
private boolean recalculationScheduled = false;
|
||||||
|
private long lastRecalculation = 0;
|
||||||
|
|
||||||
|
private void scheduleRecalculate() {
|
||||||
|
if (recalculationScheduled) return;
|
||||||
|
recalculationScheduled = true;
|
||||||
|
Bukkit.getScheduler().runTaskLater(BauSystem.getInstance(), this::recalculateIfDue, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void recalculateIfDue() {
|
||||||
|
if (System.currentTimeMillis() - lastRecalculation < RECALCULATE_INTERVAL_MS) {
|
||||||
|
Bukkit.getScheduler().runTaskLater(BauSystem.getInstance(), this::recalculateIfDue, 1);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
recalculationScheduled = false;
|
||||||
|
lastRecalculation = System.currentTimeMillis();
|
||||||
|
pistOrders.values().forEach(PistOrder::calculate);
|
||||||
|
}
|
||||||
|
|
||||||
@EventHandler
|
@EventHandler
|
||||||
public void onBlockPlace(BlockPlaceEvent event) {
|
public void onBlockPlace(BlockPlaceEvent event) {
|
||||||
if (pistOrders.isEmpty()) return;
|
if (pistOrders.isEmpty()) return;
|
||||||
Bukkit.getScheduler().runTaskLater(BauSystem.getInstance(), () -> {
|
scheduleRecalculate();
|
||||||
pistOrders.values().forEach(PistOrder::calculate);
|
|
||||||
}, 1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@EventHandler
|
@EventHandler
|
||||||
@@ -136,9 +145,7 @@ public class PistonCalculator implements Listener {
|
|||||||
PistOrder pistOrder = pistOrders.remove(event.getBlock().getLocation());
|
PistOrder pistOrder = pistOrders.remove(event.getBlock().getLocation());
|
||||||
pistOrder.server.close();
|
pistOrder.server.close();
|
||||||
}
|
}
|
||||||
Bukkit.getScheduler().runTaskLater(BauSystem.getInstance(), () -> {
|
scheduleRecalculate();
|
||||||
pistOrders.values().forEach(PistOrder::calculate);
|
|
||||||
}, 1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@EventHandler
|
@EventHandler
|
||||||
@@ -164,13 +171,13 @@ public class PistonCalculator implements Listener {
|
|||||||
private final Map<Location, PistOrder> pistOrders = new HashMap<>();
|
private final Map<Location, PistOrder> pistOrders = new HashMap<>();
|
||||||
|
|
||||||
private final class PistOrder {
|
private final class PistOrder {
|
||||||
private Block piston;
|
private final Block piston;
|
||||||
private REntityServer server = new REntityServer();
|
private REntityServer server = new REntityServer();
|
||||||
|
|
||||||
private boolean pulling = false;
|
private boolean pulling = false;
|
||||||
private List<Location> movedBlocks = new ArrayList<>();
|
private List<Location> movedBlocks = new ArrayList<>();
|
||||||
private List<Location> brokenBlocks = new ArrayList<>();
|
private List<Location> brokenBlocks = new ArrayList<>();
|
||||||
private Set<Location> immovableBlocks = new HashSet<>();
|
private boolean blocked = false;
|
||||||
|
|
||||||
public PistOrder(Block piston) {
|
public PistOrder(Block piston) {
|
||||||
this.piston = piston;
|
this.piston = piston;
|
||||||
@@ -179,7 +186,7 @@ public class PistonCalculator implements Listener {
|
|||||||
public void calculate() {
|
public void calculate() {
|
||||||
movedBlocks.clear();
|
movedBlocks.clear();
|
||||||
brokenBlocks.clear();
|
brokenBlocks.clear();
|
||||||
immovableBlocks.clear();
|
blocked = false;
|
||||||
|
|
||||||
if (piston.isEmpty()) {
|
if (piston.isEmpty()) {
|
||||||
Bukkit.getScheduler().runTaskLater(BauSystem.getInstance(), () -> {
|
Bukkit.getScheduler().runTaskLater(BauSystem.getInstance(), () -> {
|
||||||
@@ -199,7 +206,7 @@ public class PistonCalculator implements Listener {
|
|||||||
|
|
||||||
pulling = piston.getType() == Material.STICKY_PISTON && (piston.getRelative(pistonData.getFacing()).getType() == Material.AIR || pistonData.isExtended());
|
pulling = piston.getType() == Material.STICKY_PISTON && (piston.getRelative(pistonData.getFacing()).getType() == Material.AIR || pistonData.isExtended());
|
||||||
|
|
||||||
calculate(piston, pistonData.getFacing(), (pulling ? pistonData.getFacing().getOppositeFace() : pistonData.getFacing()));
|
resolve(pistonData.getFacing());
|
||||||
|
|
||||||
Collections.reverse(movedBlocks);
|
Collections.reverse(movedBlocks);
|
||||||
Collections.reverse(brokenBlocks);
|
Collections.reverse(brokenBlocks);
|
||||||
@@ -207,86 +214,23 @@ public class PistonCalculator implements Listener {
|
|||||||
visualize();
|
visualize();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void calculate(Block origin, BlockFace facing, BlockFace direction) {
|
private void resolve(BlockFace facing) {
|
||||||
Block calcOrigin = origin;
|
PistonStructureResolver resolver = new PistonStructureResolver(
|
||||||
if (facing != direction) calcOrigin = origin.getRelative(facing, 3);
|
((CraftWorld) piston.getWorld()).getHandle(),
|
||||||
|
new BlockPos(piston.getX(), piston.getY(), piston.getZ()),
|
||||||
List<Block> toCalc = new LinkedList<>();
|
Direction.valueOf(facing.name()),
|
||||||
calcDirection(origin, null, calcOrigin, facing != direction ? origin.getRelative(facing) : null, facing, direction, toCalc);
|
!pulling
|
||||||
|
);
|
||||||
while (!toCalc.isEmpty()) {
|
if (!resolver.resolve()) {
|
||||||
Block current = toCalc.remove(0);
|
blocked = true;
|
||||||
if (!movedBlocks.contains(current.getLocation())) {
|
return;
|
||||||
movedBlocks.add(current.getLocation());
|
|
||||||
}
|
|
||||||
|
|
||||||
Material type = current.getType();
|
|
||||||
if (type != Material.SLIME_BLOCK && type != Material.HONEY_BLOCK) continue;
|
|
||||||
Material oppositeType = type == Material.SLIME_BLOCK ? Material.HONEY_BLOCK : Material.SLIME_BLOCK;
|
|
||||||
|
|
||||||
for (BlockFace face : FACES) {
|
|
||||||
Block block = current.getRelative(face);
|
|
||||||
if (block.getType().isAir()) continue;
|
|
||||||
if (isImmovable(block) || block.getPistonMoveReaction() == PistonMoveReaction.BREAK) continue;
|
|
||||||
if (face != direction || face == direction.getOppositeFace()) {
|
|
||||||
switch (block.getType()) {
|
|
||||||
case BLACK_GLAZED_TERRACOTTA:
|
|
||||||
case GRAY_GLAZED_TERRACOTTA:
|
|
||||||
case BLUE_GLAZED_TERRACOTTA:
|
|
||||||
case BROWN_GLAZED_TERRACOTTA:
|
|
||||||
case CYAN_GLAZED_TERRACOTTA:
|
|
||||||
case GREEN_GLAZED_TERRACOTTA:
|
|
||||||
case LIGHT_BLUE_GLAZED_TERRACOTTA:
|
|
||||||
case LIGHT_GRAY_GLAZED_TERRACOTTA:
|
|
||||||
case LIME_GLAZED_TERRACOTTA:
|
|
||||||
case MAGENTA_GLAZED_TERRACOTTA:
|
|
||||||
case ORANGE_GLAZED_TERRACOTTA:
|
|
||||||
case PINK_GLAZED_TERRACOTTA:
|
|
||||||
case PURPLE_GLAZED_TERRACOTTA:
|
|
||||||
case RED_GLAZED_TERRACOTTA:
|
|
||||||
case WHITE_GLAZED_TERRACOTTA:
|
|
||||||
case YELLOW_GLAZED_TERRACOTTA:
|
|
||||||
continue;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (block.getType() != oppositeType) {
|
|
||||||
if (!movedBlocks.contains(block.getLocation())) toCalc.add(block);
|
|
||||||
calcDirection(null, origin, block, null, facing, direction, toCalc);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
resolver.getToPush().forEach(pos -> movedBlocks.add(toLocation(pos)));
|
||||||
movedBlocks.remove(origin.getLocation());
|
resolver.getToDestroy().forEach(pos -> brokenBlocks.add(toLocation(pos)));
|
||||||
if (facing != direction) movedBlocks.remove(origin.getRelative(facing, 1).getLocation());
|
|
||||||
if (pulling) immovableBlocks.remove(origin.getRelative(facing).getLocation());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void calcDirection(Block origin, Block blockOrigin, Block calcOrigin, Block ignore, BlockFace facing, BlockFace direction, List<Block> toCalc) {
|
private Location toLocation(BlockPos pos) {
|
||||||
for (int i = 1; i < 24; i++) {
|
return new Location(piston.getWorld(), pos.getX(), pos.getY(), pos.getZ());
|
||||||
Block block = calcOrigin.getRelative(direction, i);
|
|
||||||
if (block.equals(ignore)) return;
|
|
||||||
if (block.getPistonMoveReaction() == PistonMoveReaction.BREAK) {
|
|
||||||
if (!brokenBlocks.contains(block.getLocation())) {
|
|
||||||
brokenBlocks.add(block.getLocation());
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (isImmovable(block)) {
|
|
||||||
immovableBlocks.add(block.getLocation());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (block.getType().isAir()) return;
|
|
||||||
if (facing == direction && block.equals(blockOrigin)) {
|
|
||||||
immovableBlocks.add(block.getLocation());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (facing != direction && (block.equals(origin) || block.getRelative(facing.getOppositeFace()).equals(origin))) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!movedBlocks.contains(block.getLocation())) toCalc.add(block);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void visualize() {
|
private void visualize() {
|
||||||
@@ -321,21 +265,11 @@ public class PistonCalculator implements Listener {
|
|||||||
textDisplay.setBrightness(new Display.Brightness(15, 15));
|
textDisplay.setBrightness(new Display.Brightness(15, 15));
|
||||||
}
|
}
|
||||||
|
|
||||||
for (Location location : immovableBlocks) {
|
|
||||||
CWireframe wireframe = new CWireframe(server);
|
|
||||||
wireframe.setPos1(location);
|
|
||||||
wireframe.setPos2(location);
|
|
||||||
wireframe.setWidth(1 / 32f);
|
|
||||||
wireframe.setBlock(Material.RED_CONCRETE.createBlockData());
|
|
||||||
}
|
|
||||||
|
|
||||||
CWireframe wireframe = new CWireframe(server);
|
CWireframe wireframe = new CWireframe(server);
|
||||||
wireframe.setPos1(piston.getLocation());
|
wireframe.setPos1(piston.getLocation());
|
||||||
wireframe.setPos2(piston.getLocation());
|
wireframe.setPos2(piston.getLocation());
|
||||||
if (!immovableBlocks.isEmpty()) {
|
if (blocked) {
|
||||||
wireframe.setBlock(Material.RED_CONCRETE.createBlockData());
|
wireframe.setBlock(Material.RED_CONCRETE.createBlockData());
|
||||||
} else if (movedBlocks.size() > 12) {
|
|
||||||
wireframe.setBlock(Material.YELLOW_CONCRETE.createBlockData());
|
|
||||||
} else {
|
} else {
|
||||||
wireframe.setBlock(Material.LIME_CONCRETE.createBlockData());
|
wireframe.setBlock(Material.LIME_CONCRETE.createBlockData());
|
||||||
}
|
}
|
||||||
@@ -348,7 +282,11 @@ public class PistonCalculator implements Listener {
|
|||||||
} else {
|
} else {
|
||||||
text.append("§ePush\n");
|
text.append("§ePush\n");
|
||||||
}
|
}
|
||||||
text.append("§f").append(movedBlocks.size()).append(" §eBlocks");
|
if (blocked) {
|
||||||
|
text.append("§cBlocked");
|
||||||
|
} else {
|
||||||
|
text.append("§f").append(movedBlocks.size()).append(" §eBlocks");
|
||||||
|
}
|
||||||
textDisplay.setText(text.toString());
|
textDisplay.setText(text.toString());
|
||||||
textDisplay.setBillboard(Display.Billboard.CENTER);
|
textDisplay.setBillboard(Display.Billboard.CENTER);
|
||||||
textDisplay.setAlignment(TextDisplay.TextAlignment.CENTER);
|
textDisplay.setAlignment(TextDisplay.TextAlignment.CENTER);
|
||||||
@@ -358,26 +296,4 @@ public class PistonCalculator implements Listener {
|
|||||||
textDisplay.setBrightness(new Display.Brightness(15, 15));
|
textDisplay.setBrightness(new Display.Brightness(15, 15));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isImmovable(Block block) {
|
|
||||||
if (block.isEmpty() || block.isLiquid()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
BlockData blockData = block.getBlockData();
|
|
||||||
if (blockData instanceof Piston) {
|
|
||||||
return ((Piston) blockData).isExtended();
|
|
||||||
}
|
|
||||||
if (block.getState() instanceof TileState) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
PistonMoveReaction reaction = block.getPistonMoveReaction();
|
|
||||||
if (reaction == PistonMoveReaction.IGNORE) return true;
|
|
||||||
if (reaction == PistonMoveReaction.BLOCK) return true;
|
|
||||||
switch (block.getType()) {
|
|
||||||
case OBSIDIAN:
|
|
||||||
return true;
|
|
||||||
default:
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
+5
-9
@@ -32,26 +32,22 @@ import org.bukkit.event.EventHandler;
|
|||||||
import org.bukkit.event.Listener;
|
import org.bukkit.event.Listener;
|
||||||
import org.bukkit.event.player.PlayerInteractEntityEvent;
|
import org.bukkit.event.player.PlayerInteractEntityEvent;
|
||||||
import org.bukkit.inventory.EquipmentSlot;
|
import org.bukkit.inventory.EquipmentSlot;
|
||||||
import yapion.hierarchy.types.YAPIONObject;
|
|
||||||
import yapion.hierarchy.types.YAPIONValue;
|
|
||||||
|
|
||||||
@Linked
|
@Linked
|
||||||
public class TNTClickListener extends SWCommand implements Listener {
|
public class TNTClickListener extends SWCommand implements Listener {
|
||||||
|
|
||||||
public static final String TNT_CLICK_DETAILS = "tnt_click_details";
|
|
||||||
|
|
||||||
public TNTClickListener() {
|
public TNTClickListener() {
|
||||||
super("tntdetails");
|
super("tntdetails");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Register(description = "TNT_DETAILS_COMMAND")
|
@Register(description = "TNT_DETAILS_COMMAND")
|
||||||
public void toggle(Player player) {
|
public void toggle(Player player) {
|
||||||
YAPIONObject yapionObject = Config.getInstance().get(player);
|
Config config = Config.STORE.get(player);
|
||||||
if (yapionObject.getOrSetDefault(TNT_CLICK_DETAILS, new YAPIONValue<>(true)).asBoolean().orElse(true)) {
|
if (config.isTntClickDetails()) {
|
||||||
yapionObject.put(TNT_CLICK_DETAILS, false);
|
config.setTntClickDetails(false);
|
||||||
BauSystem.MESSAGE.send("TNT_DETAILS_OFF", player);
|
BauSystem.MESSAGE.send("TNT_DETAILS_OFF", player);
|
||||||
} else {
|
} else {
|
||||||
yapionObject.put(TNT_CLICK_DETAILS, true);
|
config.setTntClickDetails(true);
|
||||||
BauSystem.MESSAGE.send("TNT_DETAILS_ON", player);
|
BauSystem.MESSAGE.send("TNT_DETAILS_ON", player);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -60,7 +56,7 @@ public class TNTClickListener extends SWCommand implements Listener {
|
|||||||
public void onPlayerInteractEntity(PlayerInteractEntityEvent event) {
|
public void onPlayerInteractEntity(PlayerInteractEntityEvent event) {
|
||||||
if (!Permission.BUILD.hasPermission(event.getPlayer())) return;
|
if (!Permission.BUILD.hasPermission(event.getPlayer())) return;
|
||||||
if (event.getHand() != EquipmentSlot.HAND) return;
|
if (event.getHand() != EquipmentSlot.HAND) return;
|
||||||
if (!Config.getInstance().get(event.getPlayer()).getOrSetDefault(TNT_CLICK_DETAILS, new YAPIONValue<>(true)).asBoolean().orElse(true)) {
|
if (!Config.STORE.get(event.getPlayer()).isTntClickDetails()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -25,7 +25,6 @@ import lombok.Getter;
|
|||||||
import org.bukkit.*;
|
import org.bukkit.*;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
import org.bukkit.event.player.PlayerTeleportEvent;
|
import org.bukkit.event.player.PlayerTeleportEvent;
|
||||||
import yapion.hierarchy.types.YAPIONObject;
|
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
@@ -56,27 +55,22 @@ public class Warp {
|
|||||||
warpMap.put(name, this);
|
warpMap.put(name, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Warp(String name, YAPIONObject object) {
|
public Warp(String name, WorldData.WarpEntry warp) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
double x = object.getPlainValue("x");
|
location = new Location(Bukkit.getWorlds().get(0), warp.getX(), warp.getY(), warp.getZ(), warp.getYaw(), warp.getPitch());
|
||||||
double y = object.getPlainValue("y");
|
mat = Material.getMaterial(warp.getMaterial());
|
||||||
double z = object.getPlainValue("z");
|
|
||||||
float yaw = object.getPlainValue("yaw");
|
|
||||||
float pitch = object.getPlainValue("pitch");
|
|
||||||
location = new Location(Bukkit.getWorlds().get(0), x, y, z, yaw, pitch);
|
|
||||||
mat = Material.getMaterial(object.getPlainValue("material"));
|
|
||||||
warpMap.put(name, this);
|
warpMap.put(name, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public YAPIONObject output() {
|
public WorldData.WarpEntry output() {
|
||||||
YAPIONObject yapionObject = new YAPIONObject();
|
WorldData.WarpEntry warp = new WorldData.WarpEntry();
|
||||||
yapionObject.add("x", location.getX());
|
warp.setX(location.getX());
|
||||||
yapionObject.add("y", location.getY());
|
warp.setY(location.getY());
|
||||||
yapionObject.add("z", location.getZ());
|
warp.setZ(location.getZ());
|
||||||
yapionObject.add("yaw", location.getYaw());
|
warp.setYaw(location.getYaw());
|
||||||
yapionObject.add("pitch", location.getPitch());
|
warp.setPitch(location.getPitch());
|
||||||
yapionObject.add("material", mat.toString());
|
warp.setMaterial(mat.toString());
|
||||||
return yapionObject;
|
return warp;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Set<String> getWarpNames() {
|
public static Set<String> getWarpNames() {
|
||||||
@@ -101,7 +95,7 @@ public class Warp {
|
|||||||
|
|
||||||
public void delete() {
|
public void delete() {
|
||||||
warpMap.remove(name);
|
warpMap.remove(name);
|
||||||
WorldData.getWarpData().remove(name);
|
WorldData.STORE.get().getWarps().remove(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void teleport(Player player) {
|
public void teleport(Player player) {
|
||||||
|
|||||||
@@ -34,9 +34,9 @@ import net.md_5.bungee.api.chat.ComponentBuilder;
|
|||||||
import net.md_5.bungee.api.chat.HoverEvent;
|
import net.md_5.bungee.api.chat.HoverEvent;
|
||||||
import net.md_5.bungee.api.chat.TextComponent;
|
import net.md_5.bungee.api.chat.TextComponent;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
import yapion.hierarchy.types.YAPIONObject;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
@Linked
|
@Linked
|
||||||
public class WarpCommand extends SWCommand implements Disable, Enable {
|
public class WarpCommand extends SWCommand implements Disable, Enable {
|
||||||
@@ -135,17 +135,15 @@ public class WarpCommand extends SWCommand implements Disable, Enable {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void enable() {
|
public void enable() {
|
||||||
WorldData.getWarpData().forEach((name, yapionAnyType) -> {
|
WorldData.STORE.get().getWarps().forEach(Warp::new);
|
||||||
new Warp(name, (YAPIONObject) yapionAnyType);
|
|
||||||
});
|
|
||||||
Warp.enable();
|
Warp.enable();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void disable() {
|
public void disable() {
|
||||||
YAPIONObject yapionObject = WorldData.getWarpData();
|
Map<String, WorldData.WarpEntry> warps = WorldData.STORE.get().getWarps();
|
||||||
for (Warp warp : Warp.getWarps()) {
|
for (Warp warp : Warp.getWarps()) {
|
||||||
yapionObject.add(warp.getName(), warp.output());
|
warps.put(warp.getName(), warp.output());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ import de.steamwar.bausystem.utils.bossbar.BossBarService;
|
|||||||
import de.steamwar.linkage.Linked;
|
import de.steamwar.linkage.Linked;
|
||||||
import de.steamwar.network.packets.PacketHandler;
|
import de.steamwar.network.packets.PacketHandler;
|
||||||
import de.steamwar.network.packets.server.BaumemberUpdatePacket;
|
import de.steamwar.network.packets.server.BaumemberUpdatePacket;
|
||||||
|
import de.steamwar.sql.BauweltMember;
|
||||||
import io.papermc.paper.event.player.PlayerPickBlockEvent;
|
import io.papermc.paper.event.player.PlayerPickBlockEvent;
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.boss.BarColor;
|
import org.bukkit.boss.BarColor;
|
||||||
@@ -57,6 +58,8 @@ public class BauMemberUpdate extends PacketHandler implements Listener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static void baumemberUpdate() {
|
public static void baumemberUpdate() {
|
||||||
|
// Packet handlers run in no guaranteed order, so don't rely on CoreNetworkHandler having cleared the cache yet.
|
||||||
|
BauweltMember.clear();
|
||||||
Set<Player> newSpectator = new HashSet<>();
|
Set<Player> newSpectator = new HashSet<>();
|
||||||
Set<Player> newBuilder = new HashSet<>();
|
Set<Player> newBuilder = new HashSet<>();
|
||||||
Bukkit.getOnlinePlayers().forEach(player -> {
|
Bukkit.getOnlinePlayers().forEach(player -> {
|
||||||
|
|||||||
@@ -131,6 +131,7 @@ public class BauScoreboard implements Listener {
|
|||||||
|
|
||||||
@EventHandler
|
@EventHandler
|
||||||
public void onPlayerQuit(PlayerQuitEvent event) {
|
public void onPlayerQuit(PlayerQuitEvent event) {
|
||||||
|
SWScoreboard.impl.removeScoreboard(event.getPlayer());
|
||||||
ADDITIONAL_SCOREBOARD_LINES.remove(event.getPlayer());
|
ADDITIONAL_SCOREBOARD_LINES.remove(event.getPlayer());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,78 +20,39 @@
|
|||||||
package de.steamwar.bausystem.region;
|
package de.steamwar.bausystem.region;
|
||||||
|
|
||||||
import de.steamwar.bausystem.region.flags.Flag;
|
import de.steamwar.bausystem.region.flags.Flag;
|
||||||
|
import de.steamwar.bausystem.worlddata.WorldData;
|
||||||
import de.steamwar.sql.SchematicNode;
|
import de.steamwar.sql.SchematicNode;
|
||||||
import lombok.NonNull;
|
import lombok.NonNull;
|
||||||
import yapion.hierarchy.types.YAPIONObject;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.function.Function;
|
|
||||||
|
|
||||||
public abstract class RegionData {
|
public abstract class RegionData {
|
||||||
|
|
||||||
private final List<Property<?, ?>> properties = new ArrayList<>();
|
protected final WorldData.RegionEntry data;
|
||||||
|
protected final Map<String, String> flagData;
|
||||||
protected final YAPIONObject data;
|
|
||||||
protected final YAPIONObject flagData;
|
|
||||||
protected final Runnable onChange;
|
protected final Runnable onChange;
|
||||||
protected final Map<Flag<?>, Flag.Value<?>> flagMap = new HashMap<>();
|
protected final Map<Flag<?>, Flag.Value<?>> flagMap = new HashMap<>();
|
||||||
|
|
||||||
private final class Property<T, K> {
|
private SchematicNode testblockSchematic;
|
||||||
private final String field;
|
|
||||||
private final Function<K, T> loader;
|
|
||||||
private final Function<T, K> writer;
|
|
||||||
|
|
||||||
private T value;
|
protected RegionData(WorldData.RegionEntry data, Runnable onChange) {
|
||||||
|
|
||||||
public Property(String field, Function<K, T> loader, Function<T, K> writer) {
|
|
||||||
this.field = field;
|
|
||||||
this.loader = loader;
|
|
||||||
this.writer = writer;
|
|
||||||
properties.add(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void load() {
|
|
||||||
if (flagData.containsKey(field)) {
|
|
||||||
value = loader.apply(flagData.getPlainValue(field));
|
|
||||||
} else {
|
|
||||||
value = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public T get() {
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void set(T value) {
|
|
||||||
this.value = value;
|
|
||||||
if (value == null) {
|
|
||||||
flagData.remove(field);
|
|
||||||
} else {
|
|
||||||
flagData.put(field, writer.apply(value));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private Property<SchematicNode, Integer> testblockSchematic = new Property<>("testblockSchematic", SchematicNode::byId, SchematicNode::getId);
|
|
||||||
|
|
||||||
protected RegionData(YAPIONObject data, Runnable onChange) {
|
|
||||||
this.data = data;
|
this.data = data;
|
||||||
this.flagData = data.getObjectOrSetDefault("flagStorage", new YAPIONObject());
|
this.flagData = data.getFlags();
|
||||||
this.onChange = onChange;
|
this.onChange = onChange;
|
||||||
initialize();
|
initialize();
|
||||||
for (final Flag flag : Flag.getFlags()) {
|
for (final Flag flag : Flag.getFlags()) {
|
||||||
if (!has(flag).isWritable()) continue;
|
if (!has(flag).isWritable()) continue;
|
||||||
try {
|
try {
|
||||||
String s = flagData.getPlainValue(flag.name());
|
String s = flagData.get(flag.name());
|
||||||
flagMap.put(flag, flag.valueOfValue(s));
|
flagMap.put(flag, flag.valueOfValue(s));
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
flagMap.put(flag, (Flag.Value<?>) flag.getDefaultValue());
|
flagMap.put(flag, (Flag.Value<?>) flag.getDefaultValue());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
properties.forEach(Property::load);
|
if (data.getTestblockSchematic() != null) {
|
||||||
|
testblockSchematic = SchematicNode.byId(data.getTestblockSchematic());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void initialize() {
|
protected void initialize() {
|
||||||
@@ -126,7 +87,8 @@ public abstract class RegionData {
|
|||||||
flagData.remove(flag.name());
|
flagData.remove(flag.name());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
properties.forEach(property -> property.set(null));
|
testblockSchematic = null;
|
||||||
|
data.setTestblockSchematic(null);
|
||||||
onChange.run();
|
onChange.run();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -135,11 +97,12 @@ public abstract class RegionData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public SchematicNode getTestblockSchematic() {
|
public SchematicNode getTestblockSchematic() {
|
||||||
return testblockSchematic.get();
|
return testblockSchematic;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setTestblockSchematic(SchematicNode schematic) {
|
public void setTestblockSchematic(SchematicNode schematic) {
|
||||||
testblockSchematic.set(schematic);
|
testblockSchematic = schematic;
|
||||||
|
data.setTestblockSchematic(schematic == null ? null : schematic.getId());
|
||||||
onChange.run();
|
onChange.run();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,101 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is a part of the SteamWar software.
|
|
||||||
*
|
|
||||||
* Copyright (C) 2025 SteamWar.de-Serverteam
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU Affero General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program 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 Affero General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package de.steamwar.bausystem.worlddata;
|
|
||||||
|
|
||||||
import de.steamwar.sql.SteamwarUser;
|
|
||||||
import lombok.SneakyThrows;
|
|
||||||
import lombok.experimental.UtilityClass;
|
|
||||||
import org.bukkit.Bukkit;
|
|
||||||
import org.bukkit.entity.Player;
|
|
||||||
import yapion.hierarchy.output.FileOutput;
|
|
||||||
import yapion.hierarchy.types.YAPIONArray;
|
|
||||||
import yapion.hierarchy.types.YAPIONObject;
|
|
||||||
import yapion.parser.InputStreamCharsets;
|
|
||||||
import yapion.parser.YAPIONParser;
|
|
||||||
import yapion.parser.options.FileOptions;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
@UtilityClass
|
|
||||||
public class SimulatorData {
|
|
||||||
|
|
||||||
private final File simulatorsDir = new File(Bukkit.getWorlds().get(0).getWorldFolder(), "simulators");
|
|
||||||
|
|
||||||
static {
|
|
||||||
simulatorsDir.mkdirs();
|
|
||||||
}
|
|
||||||
|
|
||||||
@SneakyThrows
|
|
||||||
public YAPIONObject getSimulator(Player player) {
|
|
||||||
File file = getFile(player);
|
|
||||||
if (!file.exists()) {
|
|
||||||
return new YAPIONObject();
|
|
||||||
}
|
|
||||||
return YAPIONParser.parse(file, new FileOptions().charset(InputStreamCharsets.UTF_8));
|
|
||||||
}
|
|
||||||
|
|
||||||
@SneakyThrows
|
|
||||||
private void internalSaveSimulator(Player player, YAPIONObject yapionObject) {
|
|
||||||
File file = getFile(player);
|
|
||||||
yapionObject.toYAPION(new FileOutput(file)).close();
|
|
||||||
}
|
|
||||||
|
|
||||||
@SneakyThrows
|
|
||||||
void saveSimulator(SteamwarUser steamwarUser, YAPIONObject yapionObject) {
|
|
||||||
File file = new File(simulatorsDir, steamwarUser.getId() + ".yapion");
|
|
||||||
yapionObject.toYAPION(new FileOutput(file)).close();
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<String> listSimulator(Player player) {
|
|
||||||
List<String> strings = getSimulator(player).getKeys();
|
|
||||||
strings.remove("");
|
|
||||||
return strings;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void saveSimulator(Player player, String name) {
|
|
||||||
YAPIONObject yapionObject = getSimulator(player);
|
|
||||||
yapionObject.put(name, yapionObject.getArray("").copy());
|
|
||||||
internalSaveSimulator(player, yapionObject);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void saveSimulator(Player player, YAPIONArray simulator) {
|
|
||||||
YAPIONObject yapionObject = getSimulator(player);
|
|
||||||
yapionObject.put("", simulator);
|
|
||||||
internalSaveSimulator(player, yapionObject);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void loadSimulator(Player player, String name) {
|
|
||||||
YAPIONObject yapionObject = getSimulator(player);
|
|
||||||
yapionObject.put("", yapionObject.getArray(name).copy());
|
|
||||||
internalSaveSimulator(player, yapionObject);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void removeSimulator(Player player, String name) {
|
|
||||||
YAPIONObject yapionObject = getSimulator(player);
|
|
||||||
yapionObject.remove(name);
|
|
||||||
internalSaveSimulator(player, yapionObject);
|
|
||||||
}
|
|
||||||
|
|
||||||
private File getFile(Player player) {
|
|
||||||
SteamwarUser steamwarUser = SteamwarUser.get(player.getUniqueId());
|
|
||||||
return new File(simulatorsDir, steamwarUser.getId() + ".yapion");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* This file is a part of the SteamWar software.
|
* This file is a part of the SteamWar software.
|
||||||
*
|
*
|
||||||
* Copyright (C) 2025 SteamWar.de-Serverteam
|
* Copyright (C) 2026 SteamWar.de-Serverteam
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU Affero General Public License as published by
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
@@ -19,58 +19,83 @@
|
|||||||
|
|
||||||
package de.steamwar.bausystem.worlddata;
|
package de.steamwar.bausystem.worlddata;
|
||||||
|
|
||||||
import lombok.experimental.UtilityClass;
|
import com.google.gson.GsonBuilder;
|
||||||
|
import de.steamwar.bausystem.config.ConfigStore;
|
||||||
|
import de.steamwar.bausystem.config.VersionedConfig;
|
||||||
|
import de.steamwar.bausystem.worlddata.converter.WorldDataV1ToV2Converter;
|
||||||
|
import lombok.AccessLevel;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
import yapion.hierarchy.output.FileOutput;
|
|
||||||
import yapion.hierarchy.types.YAPIONObject;
|
|
||||||
import yapion.parser.InputStreamCharsets;
|
|
||||||
import yapion.parser.YAPIONParser;
|
|
||||||
import yapion.parser.options.StreamOptions;
|
|
||||||
|
|
||||||
import java.io.BufferedInputStream;
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileInputStream;
|
import java.util.HashMap;
|
||||||
import java.io.IOException;
|
import java.util.Map;
|
||||||
|
|
||||||
@UtilityClass
|
/**
|
||||||
public class WorldData {
|
* The current world data. It is stored as JSON, fields missing in the
|
||||||
|
* stored JSON keep the default value of their initializer, so adding a new
|
||||||
|
* field does not need a new version.
|
||||||
|
* <p>
|
||||||
|
* When a field gets removed, renamed or its meaning changes, copy this class
|
||||||
|
* to WorldDataV{@link #VERSION} (without the {@link #STORE}), increase {@link #VERSION}, write a
|
||||||
|
* {@link de.steamwar.bausystem.config.ConfigConverter} from the copy to this class and register it in the {@link #STORE}.
|
||||||
|
*/
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
public class WorldData implements VersionedConfig {
|
||||||
|
|
||||||
private final File optionsFile = new File(Bukkit.getWorlds().get(0).getWorldFolder(), "options.yapion");
|
public static final int VERSION = 2;
|
||||||
private YAPIONObject worldData;
|
|
||||||
|
|
||||||
public YAPIONObject getWorldData() {
|
public static final ConfigStore<WorldData> STORE = new ConfigStore<>(WorldData.class, VERSION, WorldData::new)
|
||||||
if (worldData == null) {
|
.gson(new GsonBuilder().setPrettyPrinting().create())
|
||||||
read();
|
.converter(new WorldDataV1ToV2Converter())
|
||||||
}
|
.file(new File(Bukkit.getWorlds().get(0).getWorldFolder(), "options.json"))
|
||||||
return worldData;
|
.legacyFile(new File(Bukkit.getWorlds().get(0).getWorldFolder(), "options.yapion"), WorldDataV1::parse);
|
||||||
|
|
||||||
|
@Setter(AccessLevel.NONE)
|
||||||
|
protected int version = VERSION;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Region name to the data of the region
|
||||||
|
*/
|
||||||
|
protected Map<String, RegionEntry> regions = new HashMap<>();
|
||||||
|
protected RegionEntry globalRegion = new RegionEntry();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Warp name to warp
|
||||||
|
*/
|
||||||
|
protected Map<String, WarpEntry> warps = new HashMap<>();
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
public static class RegionEntry {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Overrides the prototype of the region config, null if not overridden
|
||||||
|
*/
|
||||||
|
protected String prototype = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Flag name to flag value name
|
||||||
|
*/
|
||||||
|
protected Map<String, String> flags = new HashMap<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Id of the testblock SchematicNode, null if the default testblock is used
|
||||||
|
*/
|
||||||
|
protected Integer testblockSchematic = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public YAPIONObject getRegionsData() {
|
@Getter
|
||||||
return getWorldData().getYAPIONObjectOrSetDefault("regions", new YAPIONObject());
|
@Setter
|
||||||
}
|
public static class WarpEntry {
|
||||||
|
|
||||||
public YAPIONObject getWarpData() {
|
protected double x;
|
||||||
return getWorldData().getYAPIONObjectOrSetDefault("warps", new YAPIONObject());
|
protected double y;
|
||||||
}
|
protected double z;
|
||||||
|
protected float yaw;
|
||||||
private void read() {
|
protected float pitch;
|
||||||
worldData = new YAPIONObject();
|
protected String material;
|
||||||
if (optionsFile.length() != 0) {
|
|
||||||
try (BufferedInputStream bufferedInputStream = new BufferedInputStream(new FileInputStream(optionsFile))) {
|
|
||||||
worldData = YAPIONParser.parse(bufferedInputStream, new StreamOptions().charset(InputStreamCharsets.UTF_8));
|
|
||||||
} catch (IOException e) {
|
|
||||||
// Ignored
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
write();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void write() {
|
|
||||||
try {
|
|
||||||
worldData.toYAPION(new FileOutput(optionsFile)).close();
|
|
||||||
} catch (IOException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,111 @@
|
|||||||
|
/*
|
||||||
|
* This file is a part of the SteamWar software.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2026 SteamWar.de-Serverteam
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program 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 Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.steamwar.bausystem.worlddata;
|
||||||
|
|
||||||
|
import de.steamwar.bausystem.config.VersionedConfig;
|
||||||
|
import de.steamwar.bausystem.config.legacy.YapionReader;
|
||||||
|
import lombok.AccessLevel;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The legacy world data that was stored in the YAPION format in options.yapion.
|
||||||
|
*/
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
@Deprecated
|
||||||
|
public class WorldDataV1 implements VersionedConfig {
|
||||||
|
|
||||||
|
public static final int VERSION = 1;
|
||||||
|
|
||||||
|
@Setter(AccessLevel.NONE)
|
||||||
|
protected int version = VERSION;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Region name to the data of the region, the global region is stored with the name "global"
|
||||||
|
*/
|
||||||
|
protected Map<String, RegionEntry> regions = new HashMap<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Warp name to warp
|
||||||
|
*/
|
||||||
|
protected Map<String, WarpEntry> warps = new HashMap<>();
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
public static class RegionEntry {
|
||||||
|
|
||||||
|
protected String prototype = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Flag name to flag value name and "testblockSchematic" to the SchematicNode id
|
||||||
|
*/
|
||||||
|
protected Map<String, Object> flagStorage = new HashMap<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
public static class WarpEntry {
|
||||||
|
|
||||||
|
protected double x;
|
||||||
|
protected double y;
|
||||||
|
protected double z;
|
||||||
|
protected float yaw;
|
||||||
|
protected float pitch;
|
||||||
|
protected String material;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static WorldDataV1 parse(String yapion) {
|
||||||
|
Map<String, Object> object = YapionReader.parse(yapion);
|
||||||
|
WorldDataV1 worldData = new WorldDataV1();
|
||||||
|
|
||||||
|
if (object.get("regions") instanceof Map<?, ?> regions) {
|
||||||
|
regions.forEach((name, value) -> {
|
||||||
|
if (!(value instanceof Map<?, ?> region)) return;
|
||||||
|
RegionEntry entry = new RegionEntry();
|
||||||
|
if (region.get("prototype") instanceof String prototype) {
|
||||||
|
entry.prototype = prototype;
|
||||||
|
}
|
||||||
|
if (region.get("flagStorage") instanceof Map<?, ?> flagStorage) {
|
||||||
|
flagStorage.forEach((key, flag) -> entry.flagStorage.put((String) key, flag));
|
||||||
|
}
|
||||||
|
worldData.regions.put((String) name, entry);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (object.get("warps") instanceof Map<?, ?> warps) {
|
||||||
|
warps.forEach((name, value) -> {
|
||||||
|
if (!(value instanceof Map<?, ?> warp)) return;
|
||||||
|
WarpEntry entry = new WarpEntry();
|
||||||
|
entry.x = ((Number) warp.get("x")).doubleValue();
|
||||||
|
entry.y = ((Number) warp.get("y")).doubleValue();
|
||||||
|
entry.z = ((Number) warp.get("z")).doubleValue();
|
||||||
|
entry.yaw = ((Number) warp.get("yaw")).floatValue();
|
||||||
|
entry.pitch = ((Number) warp.get("pitch")).floatValue();
|
||||||
|
entry.material = (String) warp.get("material");
|
||||||
|
worldData.warps.put((String) name, entry);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return worldData;
|
||||||
|
}
|
||||||
|
}
|
||||||
+75
@@ -0,0 +1,75 @@
|
|||||||
|
/*
|
||||||
|
* This file is a part of the SteamWar software.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2026 SteamWar.de-Serverteam
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program 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 Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.steamwar.bausystem.worlddata.converter;
|
||||||
|
|
||||||
|
import de.steamwar.bausystem.config.ConfigConverter;
|
||||||
|
import de.steamwar.bausystem.worlddata.WorldData;
|
||||||
|
import de.steamwar.bausystem.worlddata.WorldDataV1;
|
||||||
|
|
||||||
|
@Deprecated
|
||||||
|
public class WorldDataV1ToV2Converter implements ConfigConverter<WorldDataV1, WorldData> {
|
||||||
|
|
||||||
|
private static final String GLOBAL_REGION = "global";
|
||||||
|
private static final String TESTBLOCK_SCHEMATIC = "testblockSchematic";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int version() {
|
||||||
|
return WorldDataV1.VERSION;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public WorldData convert(WorldDataV1 old) {
|
||||||
|
WorldData worldData = new WorldData();
|
||||||
|
old.getRegions().forEach((name, region) -> {
|
||||||
|
if (name.equals(GLOBAL_REGION)) {
|
||||||
|
worldData.setGlobalRegion(convert(region));
|
||||||
|
} else {
|
||||||
|
worldData.getRegions().put(name, convert(region));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
old.getWarps().forEach((name, warp) -> worldData.getWarps().put(name, convert(warp)));
|
||||||
|
return worldData;
|
||||||
|
}
|
||||||
|
|
||||||
|
private WorldData.RegionEntry convert(WorldDataV1.RegionEntry old) {
|
||||||
|
WorldData.RegionEntry region = new WorldData.RegionEntry();
|
||||||
|
region.setPrototype(old.getPrototype());
|
||||||
|
old.getFlagStorage().forEach((key, value) -> {
|
||||||
|
if (value == null) return;
|
||||||
|
if (key.equals(TESTBLOCK_SCHEMATIC)) {
|
||||||
|
region.setTestblockSchematic(((Number) value).intValue());
|
||||||
|
} else {
|
||||||
|
region.getFlags().put(key, value.toString());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return region;
|
||||||
|
}
|
||||||
|
|
||||||
|
private WorldData.WarpEntry convert(WorldDataV1.WarpEntry old) {
|
||||||
|
WorldData.WarpEntry warp = new WorldData.WarpEntry();
|
||||||
|
warp.setX(old.getX());
|
||||||
|
warp.setY(old.getY());
|
||||||
|
warp.setZ(old.getZ());
|
||||||
|
warp.setYaw(old.getYaw());
|
||||||
|
warp.setPitch(old.getPitch());
|
||||||
|
warp.setMaterial(old.getMaterial());
|
||||||
|
return warp;
|
||||||
|
}
|
||||||
|
}
|
||||||
+2
-2
@@ -25,12 +25,12 @@ import de.steamwar.bausystem.region.flags.ColorMode;
|
|||||||
import de.steamwar.bausystem.region.flags.Flag;
|
import de.steamwar.bausystem.region.flags.Flag;
|
||||||
import de.steamwar.bausystem.region.flags.ProtectMode;
|
import de.steamwar.bausystem.region.flags.ProtectMode;
|
||||||
import de.steamwar.bausystem.region.flags.TNTMode;
|
import de.steamwar.bausystem.region.flags.TNTMode;
|
||||||
|
import de.steamwar.bausystem.worlddata.WorldData;
|
||||||
import lombok.NonNull;
|
import lombok.NonNull;
|
||||||
import yapion.hierarchy.types.YAPIONObject;
|
|
||||||
|
|
||||||
public class FixedGlobalRegionData extends RegionData {
|
public class FixedGlobalRegionData extends RegionData {
|
||||||
|
|
||||||
public FixedGlobalRegionData(YAPIONObject data, Runnable onChange) {
|
public FixedGlobalRegionData(WorldData.RegionEntry data, Runnable onChange) {
|
||||||
super(data, onChange);
|
super(data, onChange);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+3
-2
@@ -26,6 +26,7 @@ import de.steamwar.bausystem.region.flags.Flag;
|
|||||||
import de.steamwar.bausystem.region.flags.TestblockMode;
|
import de.steamwar.bausystem.region.flags.TestblockMode;
|
||||||
import de.steamwar.bausystem.utils.FlatteningWrapper;
|
import de.steamwar.bausystem.utils.FlatteningWrapper;
|
||||||
import de.steamwar.bausystem.utils.PasteBuilder;
|
import de.steamwar.bausystem.utils.PasteBuilder;
|
||||||
|
import de.steamwar.bausystem.worlddata.WorldData;
|
||||||
import de.steamwar.core.Core;
|
import de.steamwar.core.Core;
|
||||||
import de.steamwar.sql.GameModeConfig;
|
import de.steamwar.sql.GameModeConfig;
|
||||||
import de.steamwar.sql.SchematicType;
|
import de.steamwar.sql.SchematicType;
|
||||||
@@ -139,7 +140,7 @@ public class FixedRegion implements Region {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public FixedRegion(String name, FixedRegionData flagStorage, Prototype prototype, YAPIONObject regionConfig, YAPIONObject regionData) {
|
public FixedRegion(String name, FixedRegionData flagStorage, Prototype prototype, YAPIONObject regionConfig, WorldData.RegionEntry regionData) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
uuid = UUID.nameUUIDFromBytes(name.getBytes(StandardCharsets.UTF_8));
|
uuid = UUID.nameUUIDFromBytes(name.getBytes(StandardCharsets.UTF_8));
|
||||||
this.flagStorage = flagStorage;
|
this.flagStorage = flagStorage;
|
||||||
@@ -325,7 +326,7 @@ public class FixedRegion implements Region {
|
|||||||
}
|
}
|
||||||
|
|
||||||
floorLevel = prototype.getFloorOffset() != 0 ? minPoint.getY() + prototype.getFloorOffset() : 0;
|
floorLevel = prototype.getFloorOffset() != 0 ? minPoint.getY() + prototype.getFloorOffset() : 0;
|
||||||
waterLevel = prototype.getWaterOffset() != 0 ? minPoint.getX() + prototype.getWaterOffset() : 0;
|
waterLevel = prototype.getWaterOffset() != 0 ? minPoint.getY() + prototype.getWaterOffset() : 0;
|
||||||
|
|
||||||
File found = null;
|
File found = null;
|
||||||
File baseFile = new File(BauSystem.getInstance().getDataFolder().getParentFile(), "FightSystem");
|
File baseFile = new File(BauSystem.getInstance().getDataFolder().getParentFile(), "FightSystem");
|
||||||
|
|||||||
+2
-2
@@ -22,12 +22,12 @@ package de.steamwar.bausystem.region.fixed;
|
|||||||
import de.steamwar.bausystem.region.RegionData;
|
import de.steamwar.bausystem.region.RegionData;
|
||||||
import de.steamwar.bausystem.region.RegionFlagPolicy;
|
import de.steamwar.bausystem.region.RegionFlagPolicy;
|
||||||
import de.steamwar.bausystem.region.flags.Flag;
|
import de.steamwar.bausystem.region.flags.Flag;
|
||||||
|
import de.steamwar.bausystem.worlddata.WorldData;
|
||||||
import lombok.NonNull;
|
import lombok.NonNull;
|
||||||
import yapion.hierarchy.types.YAPIONObject;
|
|
||||||
|
|
||||||
public class FixedRegionData extends RegionData {
|
public class FixedRegionData extends RegionData {
|
||||||
|
|
||||||
public FixedRegionData(YAPIONObject data, Runnable onChange) {
|
public FixedRegionData(WorldData.RegionEntry data, Runnable onChange) {
|
||||||
super(data, onChange);
|
super(data, onChange);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+4
-4
@@ -210,14 +210,14 @@ public class Prototype {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void generateRegion(String name, YAPIONObject regionConfig, YAPIONObject regionData) {
|
public static void generateRegion(String name, YAPIONObject regionConfig, WorldData.RegionEntry regionData) {
|
||||||
Prototype prototype;
|
Prototype prototype;
|
||||||
if (regionData.containsKey("prototype", String.class)) {
|
if (regionData.getPrototype() != null) {
|
||||||
prototype = PROTOTYPE_MAP.get(regionData.getPlainValue("prototype"));
|
prototype = PROTOTYPE_MAP.get(regionData.getPrototype());
|
||||||
} else {
|
} else {
|
||||||
prototype = PROTOTYPE_MAP.get(regionConfig.getPlainValue("prototype"));
|
prototype = PROTOTYPE_MAP.get(regionConfig.getPlainValue("prototype"));
|
||||||
}
|
}
|
||||||
FixedRegionData flagStorage = new FixedRegionData(regionData, WorldData::write);
|
FixedRegionData flagStorage = new FixedRegionData(regionData, WorldData.STORE::save);
|
||||||
FixedRegionSystem.addRegion(new FixedRegion(name, flagStorage, prototype, regionConfig, regionData));
|
FixedRegionSystem.addRegion(new FixedRegion(name, flagStorage, prototype, regionConfig, regionData));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-15
@@ -28,7 +28,6 @@ import org.bukkit.Bukkit;
|
|||||||
import yapion.hierarchy.diff.DiffChange;
|
import yapion.hierarchy.diff.DiffChange;
|
||||||
import yapion.hierarchy.diff.YAPIONDiff;
|
import yapion.hierarchy.diff.YAPIONDiff;
|
||||||
import yapion.hierarchy.types.YAPIONObject;
|
import yapion.hierarchy.types.YAPIONObject;
|
||||||
import yapion.hierarchy.types.YAPIONType;
|
|
||||||
import yapion.parser.YAPIONParser;
|
import yapion.parser.YAPIONParser;
|
||||||
|
|
||||||
import java.io.BufferedInputStream;
|
import java.io.BufferedInputStream;
|
||||||
@@ -55,7 +54,7 @@ public class RegionLoader {
|
|||||||
}
|
}
|
||||||
loaded = yapionObject;
|
loaded = yapionObject;
|
||||||
|
|
||||||
YAPIONObject optionsYapionObject = WorldData.getRegionsData();
|
WorldData worldData = WorldData.STORE.get();
|
||||||
yapionObject.forEach((key, yapionAnyType) -> {
|
yapionObject.forEach((key, yapionAnyType) -> {
|
||||||
if (key.equals("global")) {
|
if (key.equals("global")) {
|
||||||
return;
|
return;
|
||||||
@@ -65,21 +64,10 @@ public class RegionLoader {
|
|||||||
}
|
}
|
||||||
|
|
||||||
YAPIONObject regionConfig = (YAPIONObject) yapionAnyType;
|
YAPIONObject regionConfig = (YAPIONObject) yapionAnyType;
|
||||||
YAPIONObject regionData = new YAPIONObject();
|
WorldData.RegionEntry regionData = worldData.getRegions().computeIfAbsent(key, k -> new WorldData.RegionEntry());
|
||||||
if (optionsYapionObject.containsKey(key, YAPIONType.OBJECT)) {
|
|
||||||
regionData = optionsYapionObject.getObject(key);
|
|
||||||
} else {
|
|
||||||
optionsYapionObject.add(key, regionData);
|
|
||||||
}
|
|
||||||
|
|
||||||
Prototype.generateRegion(key, regionConfig, regionData);
|
Prototype.generateRegion(key, regionConfig, regionData);
|
||||||
});
|
});
|
||||||
|
|
||||||
YAPIONObject globalOptions = optionsYapionObject.getObject("global");
|
FixedGlobalRegion.setFLAG_STORAGE(new FixedGlobalRegionData(worldData.getGlobalRegion(), WorldData.STORE::save));
|
||||||
if (globalOptions == null) {
|
|
||||||
globalOptions = new YAPIONObject();
|
|
||||||
optionsYapionObject.add("global", globalOptions);
|
|
||||||
}
|
|
||||||
FixedGlobalRegion.setFLAG_STORAGE(new FixedGlobalRegionData(globalOptions, WorldData::write));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,40 @@
|
|||||||
|
/*
|
||||||
|
* This file is a part of the SteamWar software.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2026 SteamWar.de-Serverteam
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program 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 Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
package de.steamwar.network.packets.server;
|
||||||
|
|
||||||
|
import de.steamwar.network.packets.NetworkPacket;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A chat message written on an arena. The arena only records it for the replay and never displays it.
|
||||||
|
*/
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
@Getter
|
||||||
|
public class ChatRecordPacket extends NetworkPacket {
|
||||||
|
private static final long serialVersionUID = -4718360932583127458L;
|
||||||
|
private int playerId;
|
||||||
|
private String message;
|
||||||
|
}
|
||||||
@@ -30,6 +30,7 @@ import org.jetbrains.exposed.v1.dao.CompositeEntity
|
|||||||
import org.jetbrains.exposed.v1.dao.CompositeEntityClass
|
import org.jetbrains.exposed.v1.dao.CompositeEntityClass
|
||||||
import org.jetbrains.exposed.v1.jdbc.insertIgnore
|
import org.jetbrains.exposed.v1.jdbc.insertIgnore
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
import java.util.concurrent.ConcurrentHashMap
|
||||||
|
|
||||||
object BauweltMemberTable : CompositeIdTable("BauweltMember") {
|
object BauweltMemberTable : CompositeIdTable("BauweltMember") {
|
||||||
val bauweltId = reference("BauweltID", SteamwarUserTable).index()
|
val bauweltId = reference("BauweltID", SteamwarUserTable).index()
|
||||||
@@ -48,10 +49,12 @@ object BauweltMemberTable : CompositeIdTable("BauweltMember") {
|
|||||||
|
|
||||||
class BauweltMember(id: EntityID<CompositeID>) : CompositeEntity(id) {
|
class BauweltMember(id: EntityID<CompositeID>) : CompositeEntity(id) {
|
||||||
companion object : CompositeEntityClass<BauweltMember>(BauweltMemberTable) {
|
companion object : CompositeEntityClass<BauweltMember>(BauweltMemberTable) {
|
||||||
private val cache = mutableMapOf<Int, BauweltMember>()
|
// Keyed by (bauweltId, memberId). Optional.empty() caches "is not a member", which is the
|
||||||
|
// common case for spectators and would otherwise hit the DB on every permission check.
|
||||||
|
private val cache = ConcurrentHashMap<Pair<Int, Int>, Optional<BauweltMember>>()
|
||||||
|
|
||||||
private fun cache(member: BauweltMember) =
|
private fun cache(member: BauweltMember) =
|
||||||
cache.put(member.memberID, member)
|
cache.put(member.bauweltID to member.memberID, Optional.of(member))
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun clear() =
|
fun clear() =
|
||||||
@@ -75,20 +78,20 @@ class BauweltMember(id: EntityID<CompositeID>) : CompositeEntity(id) {
|
|||||||
it[worldEdit] = false
|
it[worldEdit] = false
|
||||||
it[world] = false
|
it[world] = false
|
||||||
}
|
}
|
||||||
}
|
}.also { cache.remove(ownerId.value to newMemberId.value) }
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
@Deprecated("Use getBauMember(bauwelt: Int, member: Int)")
|
@Deprecated("Use getBauMember(bauwelt: Int, member: Int)")
|
||||||
fun getBauMember(bauwelt: UUID, member: UUID) =
|
fun getBauMember(bauwelt: UUID, member: UUID) =
|
||||||
useDb {
|
getBauMember(SteamwarUser.get(bauwelt)!!.id.value, SteamwarUser.get(member)!!.id.value)
|
||||||
find { (bauweltId eq SteamwarUser.get(bauwelt)!!.id) and (BauweltMemberTable.memberId eq SteamwarUser.get(member)!!.id) }.firstOrNull()?.also { cache(it) }
|
|
||||||
}
|
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun getBauMember(bauwelt: Int, member: Int) =
|
fun getBauMember(bauwelt: Int, member: Int): BauweltMember? =
|
||||||
useDb {
|
cache.computeIfAbsent(bauwelt to member) {
|
||||||
find { (bauweltId eq bauwelt) and (BauweltMemberTable.memberId eq member) }.firstOrNull()?.also { cache(it) }
|
Optional.ofNullable(useDb {
|
||||||
}
|
find { (bauweltId eq bauwelt) and (BauweltMemberTable.memberId eq member) }.firstOrNull()
|
||||||
|
})
|
||||||
|
}.orElse(null)
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
@Deprecated("Use getMembers(bauwelt: Int)")
|
@Deprecated("Use getMembers(bauwelt: Int)")
|
||||||
@@ -140,8 +143,11 @@ class BauweltMember(id: EntityID<CompositeID>) : CompositeEntity(id) {
|
|||||||
fun isWorld() =
|
fun isWorld() =
|
||||||
world
|
world
|
||||||
|
|
||||||
fun remove() =
|
fun remove() {
|
||||||
|
val key = bauweltID to memberID
|
||||||
useDb {
|
useDb {
|
||||||
delete()
|
delete()
|
||||||
}
|
}
|
||||||
|
cache.remove(key)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -19,6 +19,7 @@
|
|||||||
|
|
||||||
package de.steamwar.sql;
|
package de.steamwar.sql;
|
||||||
|
|
||||||
|
import de.steamwar.sql.internal.SQLConfig;
|
||||||
import lombok.ToString;
|
import lombok.ToString;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
@@ -250,6 +251,11 @@ public final class GameModeConfig<M, W> {
|
|||||||
Times = new TimesConfig(loader.with("Times"));
|
Times = new TimesConfig(loader.with("Times"));
|
||||||
// Arena would be here to be in config order but needs Schematic.Size and EnterStages loaded afterwards
|
// Arena would be here to be in config order but needs Schematic.Size and EnterStages loaded afterwards
|
||||||
Schematic = new SchematicConfig<>(loader.with("Schematic"));
|
Schematic = new SchematicConfig<>(loader.with("Schematic"));
|
||||||
|
for (int i = 0; i < Schematic.Limited.size(); i++) {
|
||||||
|
if (Schematic.Limited.get(i).Materials.isEmpty()) {
|
||||||
|
SQLConfig.impl.getLogger().warning(file.getName() + ": Schematic.Limited[" + i + "] matches no blocks, check its 'Materials' entry");
|
||||||
|
}
|
||||||
|
}
|
||||||
GameName = loader.getString("GameName", defaultGameName.apply(file));
|
GameName = loader.getString("GameName", defaultGameName.apply(file));
|
||||||
ActiveMonths = loader.getIntList("ActiveMonths");
|
ActiveMonths = loader.getIntList("ActiveMonths");
|
||||||
TeamChatPrefix = loader.getString("TeamChatPrefix", "+");
|
TeamChatPrefix = loader.getString("TeamChatPrefix", "+");
|
||||||
@@ -611,6 +617,15 @@ public final class GameModeConfig<M, W> {
|
|||||||
*/
|
*/
|
||||||
public final List<SchematicType> SubTypes;
|
public final List<SchematicType> SubTypes;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* List of publics that should be used. Mainly useful for an Event Mode
|
||||||
|
* where a subset of the regular publics should be used without duplicating
|
||||||
|
* those in the Database.
|
||||||
|
*
|
||||||
|
* @implSpec {@code []} by default, thus the GameModes publics are used.
|
||||||
|
*/
|
||||||
|
public final List<Integer> Publics;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Shortcut of the schematic type
|
* Shortcut of the schematic type
|
||||||
*
|
*
|
||||||
@@ -703,7 +718,10 @@ public final class GameModeConfig<M, W> {
|
|||||||
public final double MaxDesignBlastResistance;
|
public final double MaxDesignBlastResistance;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* List of limited block materials
|
* List of limited block materials<br/>
|
||||||
|
* Every inventory block is counted towards exactly one rule: a filled block towards a rule whose
|
||||||
|
* {@link BlockRule#Content} allows all of its items, an empty block towards a rule without
|
||||||
|
* {@link BlockRule#Content}. Empty inventory blocks without such a rule are unlimited.
|
||||||
*
|
*
|
||||||
* @implSpec {@link SQLWrapper#getInventoryMaterials)} are all disallowed by default to have any content but are unlimited in number.
|
* @implSpec {@link SQLWrapper#getInventoryMaterials)} are all disallowed by default to have any content but are unlimited in number.
|
||||||
* @implSpec Any Block that has more Blast Resistance than {@link #MaxBlastResistance} will be defaulted to 0.
|
* @implSpec Any Block that has more Blast Resistance than {@link #MaxBlastResistance} will be defaulted to 0.
|
||||||
@@ -716,6 +734,7 @@ public final class GameModeConfig<M, W> {
|
|||||||
Inset = new InsetConfig(loader.with("Inset"));
|
Inset = new InsetConfig(loader.with("Inset"));
|
||||||
Type = null;
|
Type = null;
|
||||||
SubTypesStrings = loader.getStringList("SubTypes");
|
SubTypesStrings = loader.getStringList("SubTypes");
|
||||||
|
Publics = loader.getIntList("Publics");
|
||||||
SubTypes = new ArrayList<>();
|
SubTypes = new ArrayList<>();
|
||||||
Shortcut = loader.getString("Shortcut", "");
|
Shortcut = loader.getString("Shortcut", "");
|
||||||
Material = loader.getMaterial("Material", "STONE_BUTTON");
|
Material = loader.getMaterial("Material", "STONE_BUTTON");
|
||||||
@@ -759,18 +778,13 @@ public final class GameModeConfig<M, W> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public boolean isInventory(M material) {
|
public boolean isInventory(M material) {
|
||||||
return Limited.stream()
|
return SQLWrapper.impl.getInventoryMaterials().contains(material) || Limited.stream().anyMatch(rule -> rule.Content.loaded && rule.Materials.contains(material));
|
||||||
.filter(rule -> rule.Materials.contains(material))
|
|
||||||
.anyMatch(rule -> rule.Content.loaded);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Integer getItemAmount(M material, M item) {
|
public List<BlockRule<M>> getRules(M material) {
|
||||||
return Limited.stream()
|
return Limited.stream()
|
||||||
.filter(rule -> rule.Materials.contains(material))
|
.filter(rule -> rule.Materials.contains(material))
|
||||||
.filter(rule -> rule.Content.Items.contains(item))
|
.collect(Collectors.toList());
|
||||||
.mapToInt(rule -> rule.Content.Amount)
|
|
||||||
.max()
|
|
||||||
.orElse(0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ToString
|
@ToString
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ public interface SQLWrapper<M> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Every inventory material - the {@code all} group usable in {@code GameModeConfig}'s
|
* Every inventory material - the {@code inventory} group usable in {@code GameModeConfig}'s
|
||||||
* {@code Limited}. Expected to be a superset of {@link #getStorageMaterials()}.
|
* {@code Limited}. Expected to be a superset of {@link #getStorageMaterials()}.
|
||||||
*/
|
*/
|
||||||
default Set<M> getInventoryMaterials() {
|
default Set<M> getInventoryMaterials() {
|
||||||
|
|||||||
@@ -307,12 +307,12 @@ class SteamwarUser(id: EntityID<Int>) : IntEntity(id) {
|
|||||||
punishments[punishment] = Punishment.createPunishment(this@SteamwarUser.id.value, from, punishment, reason, time, perma)
|
punishments[punishment] = Punishment.createPunishment(this@SteamwarUser.id.value, from, punishment, reason, time, perma)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setLocale(locale: Locale?, manualeLocale: Boolean) {
|
fun setLocale(locale: Locale?, manualLocale: Boolean) {
|
||||||
if (locale == null || (this.manualLocale && !manualLocale)) return
|
if (locale == null || (this.manualLocale && !manualLocale)) return
|
||||||
|
|
||||||
useDb {
|
useDb {
|
||||||
this@SteamwarUser.locale = locale
|
this@SteamwarUser.locale = locale
|
||||||
this@SteamwarUser.manualLocale = manualeLocale
|
this@SteamwarUser.manualLocale = manualLocale
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -109,7 +109,6 @@ TEAM_READY = §aTeam ready
|
|||||||
TEAM_NOT_READY = §c§mTeam ready
|
TEAM_NOT_READY = §c§mTeam ready
|
||||||
SKIP_READY = §aSkipping to next event
|
SKIP_READY = §aSkipping to next event
|
||||||
SKIP_NOT_READY = §c§mSkipping to next event
|
SKIP_NOT_READY = §c§mSkipping to next event
|
||||||
TEAM_CHAT = {0}{1}§8» {0}{2}
|
|
||||||
CHOOSE_KIT = §eChoose kit
|
CHOOSE_KIT = §eChoose kit
|
||||||
RESPAWN = §eRespawn
|
RESPAWN = §eRespawn
|
||||||
MANAGE_PLAYERS = §cManage players
|
MANAGE_PLAYERS = §cManage players
|
||||||
|
|||||||
@@ -20,19 +20,14 @@
|
|||||||
package de.steamwar.fightsystem.ai;
|
package de.steamwar.fightsystem.ai;
|
||||||
|
|
||||||
import com.sk89q.worldedit.extent.clipboard.Clipboard;
|
import com.sk89q.worldedit.extent.clipboard.Clipboard;
|
||||||
import de.steamwar.fightsystem.Config;
|
import de.steamwar.fightsystem.fight.FightSchematic;
|
||||||
import de.steamwar.fightsystem.fight.FightTeam;
|
import de.steamwar.fightsystem.fight.FightTeam;
|
||||||
import de.steamwar.fightsystem.states.FightState;
|
import de.steamwar.fightsystem.states.FightState;
|
||||||
import de.steamwar.sql.SchematicNode;
|
import de.steamwar.sql.SchematicNode;
|
||||||
import de.steamwar.sql.SteamwarUser;
|
import de.steamwar.sql.SteamwarUser;
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Random;
|
|
||||||
|
|
||||||
public class DummyAI extends AI {
|
public class DummyAI extends AI {
|
||||||
|
|
||||||
private static final Random random = new Random();
|
|
||||||
|
|
||||||
public DummyAI(FightTeam team) {
|
public DummyAI(FightTeam team) {
|
||||||
super(team, SteamwarUser.get("public"));
|
super(team, SteamwarUser.get("public"));
|
||||||
|
|
||||||
@@ -41,8 +36,7 @@ public class DummyAI extends AI {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SchematicNode chooseSchematic() {
|
public SchematicNode chooseSchematic() {
|
||||||
List<SchematicNode> publics = SchematicNode.getAllSchematicsOfType(0, Config.GameModeConfig.Schematic.Type.toDB());
|
return FightSchematic.getPublicSchematic().orElseThrow();
|
||||||
return publics.get(random.nextInt(publics.size()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -255,7 +255,15 @@ public class GUI {
|
|||||||
private static void schemDialog(Player p, SchematicType type, boolean publicSchems, boolean unchecked) {
|
private static void schemDialog(Player p, SchematicType type, boolean publicSchems, boolean unchecked) {
|
||||||
SchematicSelector selector = new SchematicSelector(p, Config.test() ? SchematicSelector.selectSchematic() : SchematicSelector.selectSchematicType(unchecked ? type.checkType() : type), node -> {
|
SchematicSelector selector = new SchematicSelector(p, Config.test() ? SchematicSelector.selectSchematic() : SchematicSelector.selectSchematicType(unchecked ? type.checkType() : type), node -> {
|
||||||
schemSelect(p, node);
|
schemSelect(p, node);
|
||||||
});
|
}) {
|
||||||
|
@Override
|
||||||
|
protected List<SchematicNode> getSchematicList(SchematicNode parent) {
|
||||||
|
if (!publicSchems || parent != null) return super.getSchematicList(parent);
|
||||||
|
List<Integer> publics = Config.GameModeConfig.Schematic.Publics;
|
||||||
|
if (publics.isEmpty()) return super.getSchematicList(null);
|
||||||
|
return publics.stream().map(SchematicNode::byId).collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
};
|
||||||
selector.setPublicMode(publicSchems ? SchematicSelector.PublicMode.PUBLIC_ONLY : SchematicSelector.PublicMode.PRIVATE_ONLY);
|
selector.setPublicMode(publicSchems ? SchematicSelector.PublicMode.PUBLIC_ONLY : SchematicSelector.PublicMode.PRIVATE_ONLY);
|
||||||
selector.open();
|
selector.open();
|
||||||
}
|
}
|
||||||
|
|||||||
+32
-15
@@ -46,6 +46,7 @@ import org.bukkit.util.Vector;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
|
|
||||||
@@ -109,31 +110,47 @@ public class FightSchematic extends StateDependent {
|
|||||||
clipboard = null;
|
clipboard = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static final Random random = new Random();
|
||||||
|
|
||||||
|
public static Optional<SchematicNode> getPublicSchematic() {
|
||||||
|
List<Integer> publicsOverridden = Config.GameModeConfig.Schematic.Publics;
|
||||||
|
if (!publicsOverridden.isEmpty()) {
|
||||||
|
int publicId = publicsOverridden.get(random.nextInt(publicsOverridden.size()));
|
||||||
|
SchematicNode schematicNode = SchematicNode.getSchematicNode(publicId);
|
||||||
|
if (schematicNode != null) {
|
||||||
|
return Optional.of(schematicNode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
List<SchematicNode> publics = SchematicNode.getAllSchematicsOfType(0, Config.GameModeConfig.Schematic.Type.toDB());
|
||||||
|
if (publics.isEmpty()) {
|
||||||
|
for (SchematicType type : Config.GameModeConfig.Schematic.SubTypes) {
|
||||||
|
publics = SchematicNode.getAllSchematicsOfType(0, type.toDB());
|
||||||
|
if (!publics.isEmpty()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (publics.isEmpty()) {
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Optional.of(publics.get(random.nextInt(publics.size())));
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void enable() {
|
public void enable() {
|
||||||
if (FightState.getFightState() == FightState.SPECTATE) return;
|
if (FightState.getFightState() == FightState.SPECTATE) return;
|
||||||
|
|
||||||
if (clipboard == null) {
|
if (clipboard == null) {
|
||||||
List<SchematicNode> publics = SchematicNode.getAllSchematicsOfType(0, Config.GameModeConfig.Schematic.Type.toDB());
|
Optional<SchematicNode> schematicNode = getPublicSchematic();
|
||||||
if (publics.isEmpty()) {
|
if (schematicNode.isEmpty()) return;
|
||||||
for (SchematicType type : Config.GameModeConfig.Schematic.SubTypes) {
|
setSchematic(schematicNode.get());
|
||||||
publics = SchematicNode.getAllSchematicsOfType(0, type.toDB());
|
|
||||||
if (!publics.isEmpty()) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (publics.isEmpty()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
setSchematic(publics.get(new Random().nextInt(publics.size())));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ArenaMode.AntiReplay.contains(Config.mode)) {
|
if (ArenaMode.AntiReplay.contains(Config.mode)) {
|
||||||
boolean changeRotation = false;
|
boolean changeRotation = false;
|
||||||
if (Config.GameModeConfig.WinConditions.contains(Winconditions.RANDOM_ROTATE)) {
|
if (Config.GameModeConfig.WinConditions.contains(Winconditions.RANDOM_ROTATE)) {
|
||||||
changeRotation = new Random().nextBoolean();
|
changeRotation = random.nextBoolean();
|
||||||
usedRotate = rotate ^ changeRotation;
|
usedRotate = rotate ^ changeRotation;
|
||||||
}
|
}
|
||||||
if (team.isBlue()) {
|
if (team.isBlue()) {
|
||||||
|
|||||||
@@ -256,10 +256,6 @@ public class FightTeam {
|
|||||||
broadcast(player -> FightSystem.getMessage().send(message, player, params));
|
broadcast(player -> FightSystem.getMessage().send(message, player, params));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void broadcastChat(Player sender, String message) {
|
|
||||||
broadcast(player -> FightSystem.getMessage().sendPrefixless("TEAM_CHAT", player, ChatMessageType.CHAT, prefix, sender.getName(), message));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void broadcast(Consumer<Player> f) {
|
private void broadcast(Consumer<Player> f) {
|
||||||
players.forEach((uuid, fightPlayer) -> {
|
players.forEach((uuid, fightPlayer) -> {
|
||||||
Player player = Bukkit.getPlayer(uuid);
|
Player player = Bukkit.getPlayer(uuid);
|
||||||
|
|||||||
@@ -19,50 +19,41 @@
|
|||||||
|
|
||||||
package de.steamwar.fightsystem.listener;
|
package de.steamwar.fightsystem.listener;
|
||||||
|
|
||||||
import de.steamwar.fightsystem.ArenaMode;
|
|
||||||
import de.steamwar.fightsystem.Config;
|
import de.steamwar.fightsystem.Config;
|
||||||
import de.steamwar.fightsystem.FightSystem;
|
import de.steamwar.fightsystem.FightSystem;
|
||||||
import de.steamwar.fightsystem.fight.Fight;
|
import de.steamwar.fightsystem.fight.Fight;
|
||||||
import de.steamwar.fightsystem.fight.FightTeam;
|
import de.steamwar.fightsystem.fight.FightTeam;
|
||||||
import de.steamwar.fightsystem.record.GlobalRecorder;
|
import de.steamwar.fightsystem.record.GlobalRecorder;
|
||||||
import de.steamwar.fightsystem.states.FightState;
|
|
||||||
import de.steamwar.fightsystem.states.StateDependentListener;
|
|
||||||
import de.steamwar.linkage.Linked;
|
import de.steamwar.linkage.Linked;
|
||||||
|
import de.steamwar.network.packets.PacketHandler;
|
||||||
|
import de.steamwar.network.packets.server.ChatRecordPacket;
|
||||||
|
import de.steamwar.sql.SteamwarUser;
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
import org.bukkit.event.EventHandler;
|
|
||||||
import org.bukkit.event.Listener;
|
|
||||||
import org.bukkit.event.player.AsyncPlayerChatEvent;
|
|
||||||
|
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Chat is handled by Velocity. Messages written on this server are only sent here to be recorded for the replay.
|
||||||
|
*/
|
||||||
@Linked
|
@Linked
|
||||||
public class Chat implements Listener {
|
public class Chat extends PacketHandler {
|
||||||
|
|
||||||
public Chat() {
|
@Handler
|
||||||
new StateDependentListener(ArenaMode.All, FightState.All, this);
|
public void handleChatRecord(ChatRecordPacket packet) {
|
||||||
}
|
SteamwarUser user = SteamwarUser.byId(packet.getPlayerId());
|
||||||
|
String message = packet.getMessage();
|
||||||
|
FightSystem.getPlugin().getLogger().log(Level.INFO, user.getUserName() + "» " + message);
|
||||||
|
|
||||||
@EventHandler
|
Player player = Bukkit.getPlayer(user.getUUID());
|
||||||
public void handlePlayerChat(AsyncPlayerChatEvent event) {
|
FightTeam fightTeam = player != null ? Fight.getPlayerTeam(player) : null;
|
||||||
Player player = event.getPlayer();
|
|
||||||
String message = event.getMessage();
|
|
||||||
|
|
||||||
FightSystem.getPlugin().getLogger().log(Level.INFO, player.getName() + "» " + message);
|
|
||||||
FightTeam fightTeam = Fight.getPlayerTeam(player);
|
|
||||||
if (fightTeam != null) {
|
if (fightTeam != null) {
|
||||||
String teamName = fightTeam.getColoredName();
|
GlobalRecorder.getInstance().chat("PARTICIPANT_CHAT", fightTeam.getColoredName(), user.getUserName(), message);
|
||||||
if (message.startsWith(Config.GameModeConfig.TeamChatPrefix)) {
|
} else if (player != null && Config.isReferee(player)) {
|
||||||
fightTeam.broadcastChat(player, message.substring(1));
|
GlobalRecorder.getInstance().chat("FIGHTLEADER_CHAT", user.getUserName(), message);
|
||||||
} else {
|
|
||||||
broadcastChat("PARTICIPANT_CHAT", teamName, player.getName(), message);
|
|
||||||
}
|
|
||||||
} else if (Config.isReferee(player)) {
|
|
||||||
broadcastChat("FIGHTLEADER_CHAT", player.getName(), message);
|
|
||||||
} else {
|
} else {
|
||||||
broadcastChat("SPECTATOR_CHAT", player.getName(), message);
|
GlobalRecorder.getInstance().chat("SPECTATOR_CHAT", user.getUserName(), message);
|
||||||
}
|
}
|
||||||
|
|
||||||
event.setCancelled(true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void broadcastChat(String message, Object... params) {
|
public static void broadcastChat(String message, Object... params) {
|
||||||
|
|||||||
@@ -49,76 +49,341 @@ PORTAL_NO_WORLDEDIT_SELECTION = §cNo WorldEdit selection
|
|||||||
PARTICLE_INVENTORY = §0Particle
|
PARTICLE_INVENTORY = §0Particle
|
||||||
PARTICLE_DESELECT = §8No particle
|
PARTICLE_DESELECT = §8No particle
|
||||||
|
|
||||||
PARTICLE_SHOW_HAS_ALL = §8You have all particles
|
|
||||||
PARTICLE_SHOW_ALL = §aShow all
|
|
||||||
PARTICLE_SHOW_UNLOCKED = §cShow unlocked
|
|
||||||
|
|
||||||
PARTICLE_UNLOCKED_BY = §eUnlocked by
|
PARTICLE_UNLOCKED_BY = §eUnlocked by
|
||||||
PARTICLE_UNLOCKED_BY_TEAM = §fJoin a team
|
PARTICLE_UNLOCKED_BY_TEAM = §fJoin a team
|
||||||
PARTICLE_UNLOCKED_BY_SPECIFIC_TEAM = §fTeam {0}
|
PARTICLE_UNLOCKED_BY_SPECIFIC_TEAM = §fTeam {0}
|
||||||
PARTICLE_UNLOCKED_BY_SPECIFIC_TEAM_EASTER = §fTeam {0} (Easter Egg Hunt)
|
PARTICLE_UNLOCKED_BY_SPECIFIC_TEAM_EASTER = §fTeam {0} (Easter Egg Hunt)
|
||||||
PARTICLE_UNLOCKED_BY_SPECIFIC_USER = §fUser {0}
|
PARTICLE_UNLOCKED_BY_SPECIFIC_USER = §fUser {0}
|
||||||
PARTICLE_UNLOCKED_BY_SPECIFIC_USER_EASTER = §fUser {0} (Easter Egg Hunt)
|
PARTICLE_UNLOCKED_BY_SPECIFIC_USER_EASTER = §fUser {0} (Easter Egg Hunt)
|
||||||
PARTICLE_UNLOCKED_BY_EVENT = §fEvent participation
|
|
||||||
PARTICLE_UNLOCKED_BY_SERVER_TEAM = §fServer Team
|
PARTICLE_UNLOCKED_BY_SERVER_TEAM = §fServer Team
|
||||||
|
PARTICLE_UNLOCKED_BY_EVENT = §fEvent participation
|
||||||
PARTICLE_UNLOCKED_BY_EVENT_PLACEMENT = §f{0} 1., 2. or 3. Place
|
PARTICLE_UNLOCKED_BY_EVENT_PLACEMENT = §f{0} 1., 2. or 3. Place
|
||||||
PARTICLE_UNLOCKED_BY_EVENT_PARTICIPATION = §f{0}
|
PARTICLE_UNLOCKED_BY_EVENT_PARTICIPATION = §f{0}
|
||||||
PARTICLE_UNLOCKED_BY_EASTER_EGG_HUNT_HALF = §fFind 45 Easter Eggs
|
PARTICLE_UNLOCKED_BY_EASTER_EGG_HUNT_HALF = §fFind 45 Easter Eggs
|
||||||
PARTICLE_UNLOCKED_BY_EASTER_EGG_HUNT_DIFFICULTY = §fFind all Easter Eggs with difficulty {0}
|
PARTICLE_UNLOCKED_BY_EASTER_EGG_HUNT_DIFFICULTY = §fFind all Easter Eggs with difficulty {0}
|
||||||
|
|
||||||
PARTICLE_ATTRIBUTE = §eAttributes§7:
|
|
||||||
PARTICLE_ATTRIBUTE_CIRCLE = §8-§f Ring
|
|
||||||
PARTICLE_ATTRIBUTE_BI_CIRCLE = §8-§f Double ring
|
|
||||||
PARTICLE_ATTRIBUTE_TRI_CIRCLE = §8-§f Tripple ring
|
|
||||||
PARTICLE_ATTRIBUTE_CLOUD = §8-§f Cloud
|
|
||||||
PARTICLE_ATTRIBUTE_TICK = §8-§f Always active
|
|
||||||
PARTICLE_ATTRIBUTE_NON_FLOOR = §8-§f In air
|
|
||||||
PARTICLE_ATTRIBUTE_FLOOR = §8-§f On ground
|
|
||||||
PARTICLE_ATTRIBUTE_NON_FLYING = §8-§f Not flying
|
|
||||||
PARTICLE_ATTRIBUTE_FLYING = §8-§f Flying
|
|
||||||
PARTICLE_ATTRIBUTE_WING = §8-§f Wings
|
|
||||||
PARTICLE_ATTRIBUTE_SNEAKING = §8-§f Sneaking
|
|
||||||
PARTICLE_ATTRIBUTE_NON_MOVING = §8-§f Not moving
|
|
||||||
PARTICLE_ATTRIBUTE_SEPARATOR = §f
|
|
||||||
|
|
||||||
PARTICLE_SELECT = §eClick to select
|
PARTICLE_SELECT = §eClick to select
|
||||||
PARTICLE_LOCKED = §c§lLocked
|
PARTICLE_LOCKED = §c§lLocked
|
||||||
PARTICLE_DEACTIVATED = §cThere are too many players online, particles are deactivated
|
PARTICLE_DEACTIVATED = §cThere are too many players online, particles are deactivated
|
||||||
|
|
||||||
PARTICLE_SNEEZE = §aSneeze
|
PARTICLE_BACK = §7Back
|
||||||
PARTICLE_SMOKE = §7Smoke
|
PARTICLE_SLOT_OPEN = §eClick to open
|
||||||
PARTICLE_FIRE = §cFire
|
PARTICLE_SLOT_CLICK_USE = §eClick to use
|
||||||
PARTICLE_WATER = §bWater
|
PARTICLE_SLOT_CLICK_STOP = §eClick to stop using
|
||||||
PARTICLE_HEART = §cHearts
|
PARTICLE_SLOT_SHIFT_EDIT = §eShift click to edit
|
||||||
PARTICLE_NOTES = §eNotes
|
PARTICLE_SLOT_NAME = §f{0} §8(§e{1}§8/§7{2}§8)
|
||||||
PARTICLE_NAUTILUS = §aNautilus
|
PARTICLE_SLOT_TITLE = §0{0} §8({1}/{2})
|
||||||
PARTICLE_SNOWBALL = §fSnowball
|
PARTICLE_SHIFT_TO_DELETE = §7Shift click to delete
|
||||||
PARTICLE_EFFECT = §5Effect
|
PARTICLE_SECTION_DESIGN = §6§lDesign
|
||||||
PARTICLE_CAMPFIRE = §7Smoke
|
PARTICLE_SECTION_PARTICLE = §6§lParticle
|
||||||
PARTICLE_MAGIC = §5Magic
|
PARTICLE_SECTION_SHAPE = §6§lShape
|
||||||
PARTICLE_ANGRY = §4Angry
|
PARTICLE_SECTION_TIMING = §6§lTiming and position
|
||||||
PARTICLE_SLIME = §aSlime
|
PARTICLE_SECTION_CONDITIONS = §6§lConditions
|
||||||
PARTICLE_MOB = §7Mob
|
PARTICLE_FIXED_LOOK = §7This design always looks the same
|
||||||
PARTICLE_SQUID = §8Squid
|
PARTICLE_VALUE_CLICK = §eClick to change
|
||||||
PARTICLE_BUBBLE = §aBubbles
|
PARTICLE_VALUE_TOGGLE = §eClick to toggle
|
||||||
PARTICLE_HONEY = §6Honey
|
PARTICLE_VALUE_CYCLE = §eLeft click§8: §7next, §eright click§8: §7previous
|
||||||
PARTICLE_NECTAR = §6Nectar
|
PARTICLE_VALUE_SELECTED = §aSelected
|
||||||
PARTICLE_FIREWORK = §7Firework
|
PARTICLE_VALUE_RESET = §eReset to {0}
|
||||||
PARTICLE_DRAGON_BREATH = §5Dragon breath
|
PARTICLE_NUMBER_RANGE = §7Range§8: §e{0} §8- §e{1}
|
||||||
PARTICLE_DAMAGE = §5Damage
|
PARTICLE_NUMBER_TYPE = §eClick to type a value
|
||||||
PARTICLE_DOLPHIN = §dDolphin
|
PARTICLE_NUMBER_RESULT = §7→ §e{0}
|
||||||
PARTICLE_WITCH = §5Witch
|
PARTICLE_NUMBER_AT_MAX = §7→ §e{0} §8(max)
|
||||||
PARTICLE_ENCHANTING = §eEnchantment
|
PARTICLE_NUMBER_AT_MIN = §7→ §e{0} §8(min)
|
||||||
PARTICLE_HAPPY = §2Joy
|
PARTICLE_COLOR_EDITOR = §0Colours
|
||||||
PARTICLE_FLAME = §7Flames
|
PARTICLE_COLOR_REMOVE = §eClick to remove
|
||||||
PARTICLE_END_ROD = §fEnd Rod
|
PARTICLE_COLOR_ADD = §aAdd colour
|
||||||
PARTICLE_CLOUD = §7Cloud
|
PARTICLE_COLOR_HEX = §eEnter hex code
|
||||||
PARTICLE_TOTEM = §aTotem
|
PARTICLE_COLOR_HEX_EXAMPLE = §7For example #FF8800
|
||||||
PARTICLE_TOWN = §5Town
|
PARTICLE_COLOR_HEX_TITLE = Hex colour
|
||||||
PARTICLE_ENCHANTING_CIRCLE = §fEnchanted
|
PARTICLE_COLOR_HEX_INVALID = §cThat is not a hex colour like #FF8800
|
||||||
PARTICLE_WATER_FIRE = §bWater§7/§cFire
|
PARTICLE_COLOR_RANDOM = §eUse random colours
|
||||||
PARTICLE_MAGIC_ENCHANTING = §5Magic/Enchantment
|
PARTICLE_SLOT_EMPTY = §7Empty slot
|
||||||
PARTICLE_WINGS_EVIL = §5Purple wings
|
PARTICLE_SLOT_CREATE = §eClick to create
|
||||||
|
PARTICLE_SLOT_ENTRIES = §7{0} entries
|
||||||
|
PARTICLE_SLOT_ACTIVE = §aActive
|
||||||
|
PARTICLE_SLOT_VIEW = §eClick to view
|
||||||
|
PARTICLE_SLOT_ADD_ENTRY = §aAdd entry
|
||||||
|
PARTICLE_SLOT_MAX_ENTRIES = §cThis slot is full
|
||||||
|
PARTICLE_SLOT_OVER_BUDGET = §cThat would spawn too many particles for this slot
|
||||||
|
PARTICLE_SLOT_RENAME = §eRename
|
||||||
|
PARTICLE_SLOT_RENAME_TITLE = Slot name
|
||||||
|
PARTICLE_SLOT_ICON = §eChange icon
|
||||||
|
PARTICLE_SLOT_ICON_TITLE = §0Slot icon
|
||||||
|
PARTICLE_SLOT_DELETE = §cDelete slot
|
||||||
|
PARTICLE_SLOT_COST = §7Particles per tick§8: §e{0}§8/§7{1}
|
||||||
|
|
||||||
|
PARTICLE_ENTRY_EDITOR = §0{0}
|
||||||
|
PARTICLE_ENTRY_DESIGN = §7Design§8: {0}
|
||||||
|
PARTICLE_ENTRY_PARTICLE = §7Particle§8: {0}
|
||||||
|
PARTICLE_ENTRY_LOCKED = §cDesign locked, not shown
|
||||||
|
PARTICLE_ENTRY_COST = §7Particles per tick§8: §e{0}
|
||||||
|
PARTICLE_ENTRY_COST_SHARE = §7This entry§8: §e{0}
|
||||||
|
PARTICLE_ENTRY_EDIT = §eClick to edit
|
||||||
|
PARTICLE_ENTRY_DELETE = §cDelete entry
|
||||||
|
|
||||||
|
PARTICLE_DESIGN_PICKER = §0Choose design
|
||||||
|
PARTICLE_CHOICE_PICKER = §0{0}
|
||||||
|
PARTICLE_COLOR_PICKER = §0Choose colour
|
||||||
|
|
||||||
|
PARTICLE_VALUE = §7Value§8: {0}
|
||||||
|
PARTICLE_VALUE_RANDOM = §7Random colours
|
||||||
|
PARTICLE_VALUE_YES = §aYes
|
||||||
|
PARTICLE_VALUE_NO = §cNo
|
||||||
|
|
||||||
|
PARTICLE_DESIGN_SIMPLE = §fSimple
|
||||||
|
PARTICLE_DESIGN_CIRCLE = §fCircle
|
||||||
|
PARTICLE_DESIGN_WING = §fWings
|
||||||
|
PARTICLE_DESIGN_CLOUD = §fCloud
|
||||||
|
PARTICLE_DESIGN_BURST = §fBurst
|
||||||
|
PARTICLE_DESIGN_FOOTPRINTS = §fFootprints
|
||||||
|
PARTICLE_DESIGN_TEAM_LINK = §fTeam link
|
||||||
|
PARTICLE_DESIGN_PULSE_SHIMMER = §fShimmer
|
||||||
|
PARTICLE_DESIGN_HEART_BEAT = §fHeartbeat
|
||||||
|
|
||||||
|
PARTICLE_PARAM_PARTICLE = §fParticle
|
||||||
|
PARTICLE_PARAM_GLYPH_FLIGHT = §fGlyph flight
|
||||||
|
PARTICLE_PARAM_BURST_TRIGGER = §fWhen
|
||||||
|
PARTICLE_PARAM_BURST_SHAPE = §fShape
|
||||||
|
PARTICLE_PARAM_BURST_RADIUS = §fRing radius
|
||||||
|
PARTICLE_PARAM_BURST_FALL = §fMinimum fall height
|
||||||
|
PARTICLE_PARAM_FOOTPRINT_LIFETIME = §fPrint lifetime (ticks)
|
||||||
|
PARTICLE_PARAM_FOOTPRINT_STEP = §fDistance between prints
|
||||||
|
PARTICLE_PARAM_LINK_SPACING = §fDistance between particles
|
||||||
|
PARTICLE_PARAM_PULSE_SPEED = §fPulse duration (ticks)
|
||||||
|
PARTICLE_PARAM_PULSE_ROTATION = §fRotation
|
||||||
|
PARTICLE_PARAM_HEART_BEAT_SPEED = §fBeat duration (ticks)
|
||||||
|
PARTICLE_PARAM_COUNT = §fAmount
|
||||||
|
PARTICLE_PARAM_SPREAD_X = §fSpread X
|
||||||
|
PARTICLE_PARAM_SPREAD_Y = §fSpread Y
|
||||||
|
PARTICLE_PARAM_SPREAD_Z = §fSpread Z
|
||||||
|
PARTICLE_PARAM_SPEED = §fSpeed
|
||||||
|
PARTICLE_PARAM_COLORS = §fColours
|
||||||
|
PARTICLE_PARAM_BLOCK = §fBlock
|
||||||
|
PARTICLE_PARAM_TRAIL_DURATION = §fTrail duration (ticks)
|
||||||
|
PARTICLE_PARAM_TRAIL_TARGET_X = §fTrail target X
|
||||||
|
PARTICLE_PARAM_TRAIL_TARGET_Y = §fTrail target Y
|
||||||
|
PARTICLE_PARAM_TRAIL_TARGET_Z = §fTrail target Z
|
||||||
|
PARTICLE_PARAM_OFFSET_X = §fOffset X
|
||||||
|
PARTICLE_PARAM_OFFSET_Y = §fOffset Y
|
||||||
|
PARTICLE_PARAM_OFFSET_Z = §fOffset Z
|
||||||
|
PARTICLE_PARAM_INTERVAL = §fEvery n ticks
|
||||||
|
PARTICLE_PARAM_PLACEMENT = §fRelative to
|
||||||
|
PARTICLE_PARAM_TICK_MODE = §fShown
|
||||||
|
PARTICLE_PARAM_MOVEMENT = §fMovement
|
||||||
|
PARTICLE_PARAM_GLIDING = §fElytra
|
||||||
|
PARTICLE_PARAM_GROUND = §fGround
|
||||||
|
PARTICLE_PARAM_SNEAK = §fSneaking
|
||||||
|
PARTICLE_PARAM_VISIBILITY = §fVisible to
|
||||||
|
PARTICLE_PARAM_RADIUS = §fRadius
|
||||||
|
PARTICLE_PARAM_CIRCLE_SPEED = §fRotation speed
|
||||||
|
PARTICLE_PARAM_PHASE = §fStart angle
|
||||||
|
PARTICLE_PARAM_RISE = §fUp and down height
|
||||||
|
PARTICLE_PARAM_RISE_SPEED = §fUp and down speed
|
||||||
|
PARTICLE_PARAM_BOUNCE = §fBounce back
|
||||||
|
PARTICLE_PARAM_INVERTED = §fStart at the top
|
||||||
|
PARTICLE_PARAM_WING_IMAGE = §fWing shape
|
||||||
|
PARTICLE_PARAM_WING_SIZE = §fWing size
|
||||||
|
PARTICLE_PARAM_WING_DETAIL = §fPoints per wing
|
||||||
|
PARTICLE_PARAM_WING_COLORING = §fColour mode
|
||||||
|
PARTICLE_PARAM_WING_SPARKLE = §fSparkle (points shown, %)
|
||||||
|
PARTICLE_PARAM_WING_MOTION = §fMotion
|
||||||
|
|
||||||
|
PARTICLE_CHOICE_TICKMODE_MOVE = §fWhile moving
|
||||||
|
PARTICLE_CHOICE_TICKMODE_ALWAYS = §fAlways
|
||||||
|
PARTICLE_CHOICE_PLACEMENT_WORLD = §fWorld (X east, Y up, Z south)
|
||||||
|
PARTICLE_CHOICE_PLACEMENT_BODY = §fBody (X right, Y up, Z forward)
|
||||||
|
PARTICLE_CHOICE_MOVEMENT_ANY = §fAny movement
|
||||||
|
PARTICLE_CHOICE_MOVEMENT_MOVING = §fOnly while moving
|
||||||
|
PARTICLE_CHOICE_MOVEMENT_STANDING_STILL = §fOnly while standing still
|
||||||
|
PARTICLE_CHOICE_GLIDING_ANY = §fWith or without elytra
|
||||||
|
PARTICLE_CHOICE_GLIDING_GLIDING = §fOnly while gliding
|
||||||
|
PARTICLE_CHOICE_GLIDING_NOT_GLIDING = §fNot while gliding
|
||||||
|
PARTICLE_CHOICE_GROUND_ANY = §fOn ground and in air
|
||||||
|
PARTICLE_CHOICE_GROUND_ON_GROUND = §fOnly on ground
|
||||||
|
PARTICLE_CHOICE_GROUND_IN_AIR = §fOnly in air
|
||||||
|
PARTICLE_CHOICE_SNEAK_ANY = §fSneaking or not
|
||||||
|
PARTICLE_CHOICE_SNEAK_SNEAKING = §fOnly while sneaking
|
||||||
|
PARTICLE_CHOICE_SNEAK_NOT_SNEAKING = §fNot while sneaking
|
||||||
|
PARTICLE_CHOICE_VISIBILITY_EVERYONE = §fEveryone
|
||||||
|
PARTICLE_CHOICE_VISIBILITY_ONLY_SELF = §fOnly me
|
||||||
|
PARTICLE_CHOICE_VISIBILITY_ONLY_OTHERS = §fOnly others
|
||||||
|
PARTICLE_CHOICE_IMAGE_SIMPLE = §fSimple
|
||||||
|
PARTICLE_CHOICE_IMAGE_COMPLEX = §fComplex
|
||||||
|
PARTICLE_CHOICE_IMAGE_SWORD = §fSword
|
||||||
|
PARTICLE_CHOICE_IMAGE_BAT = §fBat
|
||||||
|
PARTICLE_CHOICE_IMAGE_BUTTERFLY = §fButterfly
|
||||||
|
PARTICLE_CHOICE_IMAGE_FAIRY = §fFairy
|
||||||
|
PARTICLE_CHOICE_IMAGE_MWGL = §fMiniWarGear League
|
||||||
|
PARTICLE_CHOICE_IMAGE_SW = §fSteamWar
|
||||||
|
PARTICLE_CHOICE_IMAGE_WGS = §fWarGear Season
|
||||||
|
PARTICLE_CHOICE_IMAGE_SWORD_CROSSED = §fCrossed Swords
|
||||||
|
PARTICLE_CHOICE_COLORING_OVER_TIME = §fChange over time
|
||||||
|
PARTICLE_CHOICE_COLORING_ALONG_WING = §fFrom the back to the tips
|
||||||
|
PARTICLE_CHOICE_MOTION_NONE = §fNone
|
||||||
|
PARTICLE_CHOICE_MOTION_FLAP_SLOW = §fFlap slowly
|
||||||
|
PARTICLE_CHOICE_MOTION_FLAP_FAST = §fFlap quickly
|
||||||
|
PARTICLE_CHOICE_MOTION_BEAT_ON_JUMP = §fBeat on jumping
|
||||||
|
PARTICLE_CHOICE_TRIGGER_JUMP = §fOn jumping
|
||||||
|
PARTICLE_CHOICE_TRIGGER_LAND = §fOn landing
|
||||||
|
PARTICLE_CHOICE_TRIGGER_SNEAK = §fOn sneaking
|
||||||
|
PARTICLE_CHOICE_BURSTSHAPE_PUFF = §fPuff
|
||||||
|
PARTICLE_CHOICE_BURSTSHAPE_RING = §fRing
|
||||||
|
PARTICLE_CHOICE_GLYPHFLIGHT_NORMAL = §fFloat down
|
||||||
|
PARTICLE_CHOICE_GLYPHFLIGHT_INTO_PLAYER = §fInto the player
|
||||||
|
PARTICLE_CHOICE_GLYPHFLIGHT_OUT_OF_PLAYER = §fOut of the player
|
||||||
|
|
||||||
|
# Particle types
|
||||||
|
PARTICLE_TYPE_DUST = §fDust
|
||||||
|
PARTICLE_TYPE_DUST_COLOR_TRANSITION = §fColour fading dust
|
||||||
|
PARTICLE_TYPE_ENTITY_EFFECT = §fPotion swirl
|
||||||
|
PARTICLE_TYPE_TINTED_LEAVES = §fTinted leaves
|
||||||
|
PARTICLE_TYPE_BLOCK = §fBlock breaking
|
||||||
|
PARTICLE_TYPE_BLOCK_CRUMBLE = §fBlock crumbling
|
||||||
|
PARTICLE_TYPE_BLOCK_MARKER = §fBlock marker
|
||||||
|
PARTICLE_TYPE_DUST_PILLAR = §fDust pillar
|
||||||
|
PARTICLE_TYPE_FALLING_DUST = §fFalling dust
|
||||||
|
PARTICLE_TYPE_ITEM = §fItem breaking
|
||||||
|
PARTICLE_TYPE_TRAIL = §fTrail
|
||||||
|
PARTICLE_TYPE_FLAME = §fFlame
|
||||||
|
PARTICLE_TYPE_SMALL_FLAME = §fSmall flame
|
||||||
|
PARTICLE_TYPE_SOUL_FIRE_FLAME = §fSoul fire flame
|
||||||
|
PARTICLE_TYPE_SOUL = §fSoul
|
||||||
|
PARTICLE_TYPE_LAVA = §fLava pop
|
||||||
|
PARTICLE_TYPE_SMOKE = §fSmoke
|
||||||
|
PARTICLE_TYPE_LARGE_SMOKE = §fLarge smoke
|
||||||
|
PARTICLE_TYPE_WHITE_SMOKE = §fWhite smoke
|
||||||
|
PARTICLE_TYPE_CAMPFIRE_COSY_SMOKE = §fCampfire smoke
|
||||||
|
PARTICLE_TYPE_CAMPFIRE_SIGNAL_SMOKE = §fSignal smoke
|
||||||
|
PARTICLE_TYPE_ASH = §fAsh
|
||||||
|
PARTICLE_TYPE_WHITE_ASH = §fWhite ash
|
||||||
|
PARTICLE_TYPE_POOF = §fPoof
|
||||||
|
PARTICLE_TYPE_CLOUD = §fCloud
|
||||||
|
PARTICLE_TYPE_SNEEZE = §fSneeze
|
||||||
|
PARTICLE_TYPE_SPIT = §fSpit
|
||||||
|
PARTICLE_TYPE_EXPLOSION = §fExplosion
|
||||||
|
PARTICLE_TYPE_SONIC_BOOM = §fSonic boom
|
||||||
|
PARTICLE_TYPE_SWEEP_ATTACK = §fSweep attack
|
||||||
|
PARTICLE_TYPE_GUST = §fGust
|
||||||
|
PARTICLE_TYPE_SMALL_GUST = §fSmall gust
|
||||||
|
PARTICLE_TYPE_DRIPPING_WATER = §fDripping water
|
||||||
|
PARTICLE_TYPE_FALLING_WATER = §fFalling water
|
||||||
|
PARTICLE_TYPE_DRIPPING_LAVA = §fDripping lava
|
||||||
|
PARTICLE_TYPE_FALLING_LAVA = §fFalling lava
|
||||||
|
PARTICLE_TYPE_LANDING_LAVA = §fLanding lava
|
||||||
|
PARTICLE_TYPE_DRIPPING_HONEY = §fDripping honey
|
||||||
|
PARTICLE_TYPE_FALLING_HONEY = §fFalling honey
|
||||||
|
PARTICLE_TYPE_LANDING_HONEY = §fLanding honey
|
||||||
|
PARTICLE_TYPE_FALLING_NECTAR = §fFalling nectar
|
||||||
|
PARTICLE_TYPE_DRIPPING_OBSIDIAN_TEAR = §fDripping obsidian tear
|
||||||
|
PARTICLE_TYPE_FALLING_OBSIDIAN_TEAR = §fFalling obsidian tear
|
||||||
|
PARTICLE_TYPE_LANDING_OBSIDIAN_TEAR = §fLanding obsidian tear
|
||||||
|
PARTICLE_TYPE_DRIPPING_DRIPSTONE_WATER = §fDripstone water
|
||||||
|
PARTICLE_TYPE_FALLING_DRIPSTONE_WATER = §fFalling dripstone water
|
||||||
|
PARTICLE_TYPE_DRIPPING_DRIPSTONE_LAVA = §fDripstone lava
|
||||||
|
PARTICLE_TYPE_FALLING_DRIPSTONE_LAVA = §fFalling dripstone lava
|
||||||
|
PARTICLE_TYPE_RAIN = §fRain
|
||||||
|
PARTICLE_TYPE_SPLASH = §fSplash
|
||||||
|
PARTICLE_TYPE_FISHING = §fFishing wake
|
||||||
|
PARTICLE_TYPE_UNDERWATER = §fUnderwater
|
||||||
|
PARTICLE_TYPE_BUBBLE_POP = §fBubble pop
|
||||||
|
PARTICLE_TYPE_DOLPHIN = §fDolphin trail
|
||||||
|
PARTICLE_TYPE_NAUTILUS = §fNautilus
|
||||||
|
PARTICLE_TYPE_SQUID_INK = §fSquid ink
|
||||||
|
PARTICLE_TYPE_GLOW_SQUID_INK = §fGlow squid ink
|
||||||
|
PARTICLE_TYPE_GLOW = §fGlow
|
||||||
|
PARTICLE_TYPE_FALLING_SPORE_BLOSSOM = §fFalling spore blossom
|
||||||
|
PARTICLE_TYPE_SPORE_BLOSSOM_AIR = §fFloating spores
|
||||||
|
PARTICLE_TYPE_CHERRY_LEAVES = §fCherry leaves
|
||||||
|
PARTICLE_TYPE_PALE_OAK_LEAVES = §fPale oak leaves
|
||||||
|
PARTICLE_TYPE_CRIMSON_SPORE = §fCrimson spores
|
||||||
|
PARTICLE_TYPE_WARPED_SPORE = §fWarped spores
|
||||||
|
PARTICLE_TYPE_MYCELIUM = §fMycelium
|
||||||
|
PARTICLE_TYPE_COMPOSTER = §fComposter
|
||||||
|
PARTICLE_TYPE_FIREFLY = §fFirefly
|
||||||
|
PARTICLE_TYPE_SNOWFLAKE = §fSnowflake
|
||||||
|
PARTICLE_TYPE_ITEM_SNOWBALL = §fSnowball
|
||||||
|
PARTICLE_TYPE_ITEM_SLIME = §fSlime
|
||||||
|
PARTICLE_TYPE_ITEM_COBWEB = §fCobweb
|
||||||
|
PARTICLE_TYPE_EGG_CRACK = §fEgg crack
|
||||||
|
PARTICLE_TYPE_DUST_PLUME = §fDust plume
|
||||||
|
PARTICLE_TYPE_INFESTED = §fInfested
|
||||||
|
PARTICLE_TYPE_HEART = §fHeart
|
||||||
|
PARTICLE_TYPE_NOTE = §fNote
|
||||||
|
PARTICLE_TYPE_HAPPY_VILLAGER = §fHappy villager
|
||||||
|
PARTICLE_TYPE_ANGRY_VILLAGER = §fAngry villager
|
||||||
|
PARTICLE_TYPE_DAMAGE_INDICATOR = §fDamage
|
||||||
|
PARTICLE_TYPE_CRIT = §fCritical hit
|
||||||
|
PARTICLE_TYPE_ENCHANTED_HIT = §fEnchanted hit
|
||||||
|
PARTICLE_TYPE_ENCHANT = §fEnchanting glyphs
|
||||||
|
PARTICLE_TYPE_EFFECT = §fPotion effect
|
||||||
|
PARTICLE_TYPE_INSTANT_EFFECT = §fInstant effect
|
||||||
|
PARTICLE_TYPE_WITCH = §fWitch magic
|
||||||
|
PARTICLE_TYPE_END_ROD = §fEnd rod
|
||||||
|
PARTICLE_TYPE_PORTAL = §fPortal
|
||||||
|
PARTICLE_TYPE_REVERSE_PORTAL = §fReverse portal
|
||||||
|
PARTICLE_TYPE_DRAGON_BREATH = §fDragon breath
|
||||||
|
PARTICLE_TYPE_FIREWORK = §fFirework spark
|
||||||
|
PARTICLE_TYPE_TOTEM_OF_UNDYING = §fTotem
|
||||||
|
PARTICLE_TYPE_ELECTRIC_SPARK = §fElectric spark
|
||||||
|
PARTICLE_TYPE_SCRAPE = §fScrape
|
||||||
|
PARTICLE_TYPE_WAX_ON = §fWax on
|
||||||
|
PARTICLE_TYPE_WAX_OFF = §fWax off
|
||||||
|
PARTICLE_TYPE_SCULK_SOUL = §fSculk soul
|
||||||
|
PARTICLE_TYPE_SCULK_CHARGE = §fSculk charge
|
||||||
|
PARTICLE_TYPE_SCULK_CHARGE_POP = §fSculk charge pop
|
||||||
|
PARTICLE_TYPE_SHRIEK = §fShriek
|
||||||
|
PARTICLE_TYPE_RAID_OMEN = §fRaid omen
|
||||||
|
PARTICLE_TYPE_TRIAL_OMEN = §fTrial omen
|
||||||
|
PARTICLE_TYPE_OMINOUS_SPAWNING = §fOminous spawning
|
||||||
|
PARTICLE_TYPE_TRIAL_SPAWNER_DETECTION = §fTrial spawner flame
|
||||||
|
PARTICLE_TYPE_TRIAL_SPAWNER_DETECTION_OMINOUS = §fOminous trial spawner flame
|
||||||
|
PARTICLE_TYPE_VAULT_CONNECTION = §fVault connection
|
||||||
|
|
||||||
|
# Particle blocks
|
||||||
|
PARTICLE_CHOICE_BLOCK_STONE = §fStone
|
||||||
|
PARTICLE_CHOICE_BLOCK_COBBLESTONE = §fCobblestone
|
||||||
|
PARTICLE_CHOICE_BLOCK_DIRT = §fDirt
|
||||||
|
PARTICLE_CHOICE_BLOCK_GRASS_BLOCK = §fGrass block
|
||||||
|
PARTICLE_CHOICE_BLOCK_SAND = §fSand
|
||||||
|
PARTICLE_CHOICE_BLOCK_RED_SAND = §fRed sand
|
||||||
|
PARTICLE_CHOICE_BLOCK_GRAVEL = §fGravel
|
||||||
|
PARTICLE_CHOICE_BLOCK_OAK_PLANKS = §fOak planks
|
||||||
|
PARTICLE_CHOICE_BLOCK_OAK_LEAVES = §fOak leaves
|
||||||
|
PARTICLE_CHOICE_BLOCK_CHERRY_LEAVES = §fCherry leaves
|
||||||
|
PARTICLE_CHOICE_BLOCK_SNOW_BLOCK = §fSnow block
|
||||||
|
PARTICLE_CHOICE_BLOCK_ICE = §fIce
|
||||||
|
PARTICLE_CHOICE_BLOCK_GLASS = §fGlass
|
||||||
|
PARTICLE_CHOICE_BLOCK_TNT = §fTNT
|
||||||
|
PARTICLE_CHOICE_BLOCK_PUMPKIN = §fPumpkin
|
||||||
|
PARTICLE_CHOICE_BLOCK_MELON = §fMelon
|
||||||
|
PARTICLE_CHOICE_BLOCK_HONEY_BLOCK = §fHoney block
|
||||||
|
PARTICLE_CHOICE_BLOCK_SLIME_BLOCK = §fSlime block
|
||||||
|
PARTICLE_CHOICE_BLOCK_NETHERRACK = §fNetherrack
|
||||||
|
PARTICLE_CHOICE_BLOCK_SOUL_SAND = §fSoul sand
|
||||||
|
PARTICLE_CHOICE_BLOCK_MAGMA_BLOCK = §fMagma block
|
||||||
|
PARTICLE_CHOICE_BLOCK_END_STONE = §fEnd stone
|
||||||
|
PARTICLE_CHOICE_BLOCK_OBSIDIAN = §fObsidian
|
||||||
|
PARTICLE_CHOICE_BLOCK_PRISMARINE = §fPrismarine
|
||||||
|
PARTICLE_CHOICE_BLOCK_SCULK = §fSculk
|
||||||
|
PARTICLE_CHOICE_BLOCK_AMETHYST_BLOCK = §fAmethyst block
|
||||||
|
PARTICLE_CHOICE_BLOCK_COAL_BLOCK = §fBlock of coal
|
||||||
|
PARTICLE_CHOICE_BLOCK_IRON_BLOCK = §fBlock of iron
|
||||||
|
PARTICLE_CHOICE_BLOCK_GOLD_BLOCK = §fBlock of gold
|
||||||
|
PARTICLE_CHOICE_BLOCK_REDSTONE_BLOCK = §fBlock of redstone
|
||||||
|
PARTICLE_CHOICE_BLOCK_LAPIS_BLOCK = §fBlock of lapis lazuli
|
||||||
|
PARTICLE_CHOICE_BLOCK_EMERALD_BLOCK = §fBlock of emerald
|
||||||
|
PARTICLE_CHOICE_BLOCK_DIAMOND_BLOCK = §fBlock of diamond
|
||||||
|
PARTICLE_CHOICE_BLOCK_WHITE_WOOL = §fWhite wool
|
||||||
|
PARTICLE_CHOICE_BLOCK_RED_WOOL = §fRed wool
|
||||||
|
PARTICLE_CHOICE_BLOCK_ORANGE_WOOL = §fOrange wool
|
||||||
|
PARTICLE_CHOICE_BLOCK_YELLOW_WOOL = §fYellow wool
|
||||||
|
PARTICLE_CHOICE_BLOCK_LIME_WOOL = §fLime wool
|
||||||
|
PARTICLE_CHOICE_BLOCK_LIGHT_BLUE_WOOL = §fLight blue wool
|
||||||
|
PARTICLE_CHOICE_BLOCK_BLUE_WOOL = §fBlue wool
|
||||||
|
PARTICLE_CHOICE_BLOCK_PURPLE_WOOL = §fPurple wool
|
||||||
|
PARTICLE_CHOICE_BLOCK_PINK_WOOL = §fPink wool
|
||||||
|
PARTICLE_CHOICE_BLOCK_BLACK_WOOL = §fBlack wool
|
||||||
|
|
||||||
PARTICLE_PLAYER_HAYLIM_AURA = §fHaylim\'s Aura
|
PARTICLE_PLAYER_HAYLIM_AURA = §fHaylim\'s Aura
|
||||||
PARTICLE_PLAYER_1063 = §7Circling Circle
|
PARTICLE_PLAYER_1063 = §7Circling Circle
|
||||||
@@ -136,34 +401,12 @@ PARTICLE_TEAM_PULSE_AURA_3 = §fPulse Aura §fEnchanted
|
|||||||
PARTICLE_TEAM_PULSE_LOGO = §fPulse Logo
|
PARTICLE_TEAM_PULSE_LOGO = §fPulse Logo
|
||||||
PARTICLE_TEAM_PULSE_HEART_BEAT = §cHeart Beat
|
PARTICLE_TEAM_PULSE_HEART_BEAT = §cHeart Beat
|
||||||
|
|
||||||
PARTICLE_RAINCLOUD_NORMAL = §fRaincloud
|
|
||||||
PARTICLE_RAINCLOUD_RED = §fLava cloud
|
|
||||||
PARTICLE_RAINCLOUD_YELLOW = §fNectar cloud
|
|
||||||
PARTICLE_RAINCLOUD_ORANGE = §fHoney cloud
|
|
||||||
PARTICLE_RAINCLOUD_GREEN = §fSpore Blossom cloud
|
|
||||||
PARTICLE_RAINCLOUD_PURPLE = §fObsidian Tears cloud
|
|
||||||
PARTICLE_RAINCLOUD_NAUTILUS = §fNautilus cloud
|
|
||||||
PARTICLE_RAINCLOUD_ENCHANTMENT = §fEnchantment cloud
|
|
||||||
PARTICLE_RAINCLOUD_SLIME = §fSlime cloud
|
|
||||||
PARTICLE_RAINCLOUD_HAIL = §fHail cloud
|
|
||||||
PARTICLE_RAINCLOUD_SNOW = §fSnow cloud
|
|
||||||
PARTICLE_RAINCLOUD_TOTEM = §fTotem cloud
|
|
||||||
|
|
||||||
PARTICLE_EVENT_ENCHANTING = §cEnchantment
|
PARTICLE_EVENT_ENCHANTING = §cEnchantment
|
||||||
PARTICLE_EVENT_CLOUD = §fClouds
|
|
||||||
PARTICLE_EVENT_SMOKE = §7Smoke
|
|
||||||
PARTICLE_EVENT_WATER = §bWater
|
|
||||||
PARTICLE_EVENT_WINGS = §fWings
|
|
||||||
PARTICLE_EVENT_RAIN_CLOUD = §fRandom Raincloud
|
PARTICLE_EVENT_RAIN_CLOUD = §fRandom Raincloud
|
||||||
PARTICLE_EVENT_WGS = §fWGS
|
|
||||||
PARTICLE_EVENT_WARGEARCLASH = §fClash
|
|
||||||
|
|
||||||
PARTICLE_EGG_HUNT_EASY = §aGreenery
|
|
||||||
PARTICLE_EGG_HUNT_MEDIUM = §eHide and Seek
|
PARTICLE_EGG_HUNT_MEDIUM = §eHide and Seek
|
||||||
PARTICLE_EGG_HUNT_HARD = §cOrbit
|
PARTICLE_EGG_HUNT_HARD = §cOrbit
|
||||||
PARTICLE_EGG_HUNT_EXTREME = §5Purple rain
|
|
||||||
PARTICLE_EGG_HUNT_ADVANCED = §5Lift off
|
PARTICLE_EGG_HUNT_ADVANCED = §5Lift off
|
||||||
PARTICLE_EGG_HUNT_HALF = §fThe hunt is on
|
|
||||||
|
|
||||||
JUMP_AND_RUN_PROGRESS = §e{0}§8/§f{1} §c{2} §7{3}
|
JUMP_AND_RUN_PROGRESS = §e{0}§8/§f{1} §c{2} §7{3}
|
||||||
JUMP_AND_RUN_CANCEL = {0}
|
JUMP_AND_RUN_CANCEL = {0}
|
||||||
|
|||||||
@@ -49,109 +49,353 @@ PORTAL_NO_WORLDEDIT_SELECTION = §cKeine WorldEdit Selection
|
|||||||
PARTICLE_INVENTORY = §0Partikel
|
PARTICLE_INVENTORY = §0Partikel
|
||||||
PARTICLE_DESELECT = §8Keine Partikel
|
PARTICLE_DESELECT = §8Keine Partikel
|
||||||
|
|
||||||
PARTICLE_SHOW_HAS_ALL = §8Du hast alle Partikel
|
|
||||||
PARTICLE_SHOW_ALL = §aZeige alle
|
|
||||||
PARTICLE_SHOW_UNLOCKED = §cZeige freigeschaltete
|
|
||||||
|
|
||||||
PARTICLE_UNLOCKED_BY = §eFreigeschaltet durch
|
PARTICLE_UNLOCKED_BY = §eFreigeschaltet durch
|
||||||
PARTICLE_UNLOCKED_BY_TEAM = §fTeambeitritt
|
PARTICLE_UNLOCKED_BY_TEAM = §fTeambeitritt
|
||||||
PARTICLE_UNLOCKED_BY_SPECIFIC_TEAM = §fTeam {0}
|
PARTICLE_UNLOCKED_BY_SPECIFIC_TEAM = §fTeam {0}
|
||||||
PARTICLE_UNLOCKED_BY_SPECIFIC_TEAM_EASTER = §fTeam {0} (Oster-Eierer-Suche)
|
PARTICLE_UNLOCKED_BY_SPECIFIC_TEAM_EASTER = §fTeam {0} (Oster-Eierer-Suche)
|
||||||
PARTICLE_UNLOCKED_BY_SPECIFIC_USER = §fUser {0}
|
PARTICLE_UNLOCKED_BY_SPECIFIC_USER = §fUser {0}
|
||||||
PARTICLE_UNLOCKED_BY_SPECIFIC_USER_EASTER = §fUser {0} (Oster-Eierer-Suche)
|
PARTICLE_UNLOCKED_BY_SPECIFIC_USER_EASTER = §fUser {0} (Oster-Eierer-Suche)
|
||||||
PARTICLE_UNLOCKED_BY_EVENT = §fEventteilnahme
|
|
||||||
PARTICLE_UNLOCKED_BY_SERVER_TEAM = §fServerteam
|
PARTICLE_UNLOCKED_BY_SERVER_TEAM = §fServerteam
|
||||||
|
PARTICLE_UNLOCKED_BY_EVENT = §fEventteilnahme
|
||||||
PARTICLE_UNLOCKED_BY_EVENT_PLACEMENT = §f{0} 1., 2. oder 3. Platz
|
PARTICLE_UNLOCKED_BY_EVENT_PLACEMENT = §f{0} 1., 2. oder 3. Platz
|
||||||
PARTICLE_UNLOCKED_BY_EVENT_PARTICIPATION = §f{0}
|
PARTICLE_UNLOCKED_BY_EVENT_PARTICIPATION = §f{0}
|
||||||
PARTICLE_UNLOCKED_BY_EASTER_EGG_HUNT_HALF = §fFinde 45 Oster-Eierer
|
PARTICLE_UNLOCKED_BY_EASTER_EGG_HUNT_HALF = §fFinde 45 Oster-Eierer
|
||||||
PARTICLE_UNLOCKED_BY_EASTER_EGG_HUNT_DIFFICULTY = §fAlle Eierer mit Schwierigkeit {0} finden
|
PARTICLE_UNLOCKED_BY_EASTER_EGG_HUNT_DIFFICULTY = §fAlle Eierer mit Schwierigkeit {0} finden
|
||||||
|
|
||||||
PARTICLE_ATTRIBUTE_CIRCLE = §8-§f Ring
|
|
||||||
PARTICLE_ATTRIBUTE_BI_CIRCLE = §8-§f Doppelring
|
|
||||||
PARTICLE_ATTRIBUTE_TRI_CIRCLE = §8-§f Dreifachring
|
|
||||||
PARTICLE_ATTRIBUTE_CLOUD = §8-§f Wolke
|
|
||||||
PARTICLE_ATTRIBUTE_TICK = §8-§f Immer aktiv
|
|
||||||
PARTICLE_ATTRIBUTE_NON_FLOOR = §8-§f In der Luft
|
|
||||||
PARTICLE_ATTRIBUTE_FLOOR = §8-§f Auf dem Boden
|
|
||||||
PARTICLE_ATTRIBUTE_NON_FLYING = §8-§f Nicht am Fliegen
|
|
||||||
PARTICLE_ATTRIBUTE_FLYING = §8-§f Am Fliegen
|
|
||||||
PARTICLE_ATTRIBUTE_WING = §8-§f Flügel
|
|
||||||
PARTICLE_ATTRIBUTE_SNEAKING = §8-§f Ducken
|
|
||||||
PARTICLE_ATTRIBUTE_NON_MOVING = §8-§f Beim Stehen
|
|
||||||
PARTICLE_ATTRIBUTE = §eAttribute§7:
|
|
||||||
|
|
||||||
PARTICLE_SELECT = §eZum Auswählen klicken
|
PARTICLE_SELECT = §eZum Auswählen klicken
|
||||||
PARTICLE_LOCKED = §c§lGesperrt
|
PARTICLE_LOCKED = §c§lGesperrt
|
||||||
PARTICLE_DEACTIVATED = §cEs sind zu viele Spieler online! Partikel sind deaktiviert.
|
PARTICLE_DEACTIVATED = §cEs sind zu viele Spieler online! Partikel sind deaktiviert.
|
||||||
|
|
||||||
PARTICLE_SNEEZE = §aSneeze
|
PARTICLE_BACK = §7Zurück
|
||||||
PARTICLE_SMOKE = §7Rauch
|
PARTICLE_SLOT_OPEN = §eZum Öffnen klicken
|
||||||
PARTICLE_FIRE = §cFeuer
|
PARTICLE_SLOT_CLICK_USE = §eZum Verwenden klicken
|
||||||
PARTICLE_WATER = §bWasser
|
PARTICLE_SLOT_CLICK_STOP = §eZum Beenden klicken
|
||||||
PARTICLE_HEART = §cHerzen
|
PARTICLE_SLOT_SHIFT_EDIT = §eZum Bearbeiten Shift-Klick
|
||||||
PARTICLE_NOTES = §eNoten
|
PARTICLE_SLOT_NAME = §f{0} §8(§e{1}§8/§7{2}§8)
|
||||||
PARTICLE_NAUTILUS = §aNautilus
|
PARTICLE_SLOT_TITLE = §0{0} §8({1}/{2})
|
||||||
PARTICLE_SNOWBALL = §fSchneeball
|
PARTICLE_SHIFT_TO_DELETE = §7Zum Löschen Shift-Klick
|
||||||
PARTICLE_EFFECT = §5Effekt
|
PARTICLE_SECTION_DESIGN = §6§lDesign
|
||||||
PARTICLE_CAMPFIRE = §7Rauch
|
PARTICLE_SECTION_PARTICLE = §6§lPartikel
|
||||||
PARTICLE_MAGIC = §5Magie
|
PARTICLE_SECTION_SHAPE = §6§lForm
|
||||||
PARTICLE_ANGRY = §4Wut
|
PARTICLE_SECTION_TIMING = §6§lZeit und Position
|
||||||
PARTICLE_SLIME = §aSchleim
|
PARTICLE_SECTION_CONDITIONS = §6§lBedingungen
|
||||||
PARTICLE_MOB = §7Mob
|
PARTICLE_FIXED_LOOK = §7Dieses Design sieht immer gleich aus
|
||||||
PARTICLE_SQUID = §8Tintenfisch
|
PARTICLE_VALUE_CLICK = §eZum Ändern klicken
|
||||||
PARTICLE_BUBBLE = §aBlasen
|
PARTICLE_VALUE_TOGGLE = §eZum Umschalten klicken
|
||||||
PARTICLE_HONEY = §6Honig
|
PARTICLE_VALUE_CYCLE = §eLinksklick§8: §7weiter, §eRechtsklick§8: §7zurück
|
||||||
PARTICLE_NECTAR = §6Nektar
|
PARTICLE_VALUE_SELECTED = §aAusgewählt
|
||||||
PARTICLE_FIREWORK = §7Feuerwerk
|
PARTICLE_VALUE_RESET = §eAuf {0} zurücksetzen
|
||||||
PARTICLE_DRAGON_BREATH = §5Drachenatem
|
PARTICLE_NUMBER_RANGE = §7Bereich§8: §e{0} §8- §e{1}
|
||||||
PARTICLE_DAMAGE = §5Schaden
|
PARTICLE_NUMBER_TYPE = §eZum Eingeben klicken
|
||||||
PARTICLE_DOLPHIN = §dDelphin
|
PARTICLE_NUMBER_RESULT = §7→ §e{0}
|
||||||
PARTICLE_WITCH = §5Hexe
|
PARTICLE_NUMBER_AT_MAX = §7→ §e{0} §8(Max)
|
||||||
PARTICLE_ENCHANTING = §eZauber
|
PARTICLE_NUMBER_AT_MIN = §7→ §e{0} §8(Min)
|
||||||
PARTICLE_HAPPY = §2Freude
|
PARTICLE_COLOR_EDITOR = §0Farben
|
||||||
PARTICLE_FLAME = §7Flammen
|
PARTICLE_COLOR_REMOVE = §eZum Entfernen klicken
|
||||||
PARTICLE_END_ROD = §fEnd Rod
|
PARTICLE_COLOR_ADD = §aFarbe hinzufügen
|
||||||
PARTICLE_CLOUD = §7Wolke
|
PARTICLE_COLOR_HEX = §eHex-Code eingeben
|
||||||
PARTICLE_TOTEM = §aTotem
|
PARTICLE_COLOR_HEX_EXAMPLE = §7Zum Beispiel #FF8800
|
||||||
PARTICLE_TOWN = §5Town
|
PARTICLE_COLOR_HEX_TITLE = Hex-Farbe
|
||||||
PARTICLE_ENCHANTING_CIRCLE = §fVerzaubert
|
PARTICLE_COLOR_HEX_INVALID = §cDas ist keine Hex-Farbe wie #FF8800
|
||||||
PARTICLE_WATER_FIRE = §bWasser§7/§cFeuer
|
PARTICLE_COLOR_RANDOM = §eZufällige Farben verwenden
|
||||||
PARTICLE_MAGIC_ENCHANTING = §5Magie§7/§eZauber
|
PARTICLE_SLOT_EMPTY = §7Leerer Slot
|
||||||
PARTICLE_WINGS_EVIL = §5Lila Flügel
|
PARTICLE_SLOT_CREATE = §eZum Erstellen klicken
|
||||||
|
PARTICLE_SLOT_ENTRIES = §7{0} Einträge
|
||||||
|
PARTICLE_SLOT_ACTIVE = §aAktiv
|
||||||
|
PARTICLE_SLOT_VIEW = §eZum Ansehen klicken
|
||||||
|
PARTICLE_SLOT_ADD_ENTRY = §aEintrag hinzufügen
|
||||||
|
PARTICLE_SLOT_MAX_ENTRIES = §cDieser Slot ist voll
|
||||||
|
PARTICLE_SLOT_OVER_BUDGET = §cDamit würde dieser Slot zu viele Partikel erzeugen
|
||||||
|
PARTICLE_SLOT_RENAME = §eUmbenennen
|
||||||
|
PARTICLE_SLOT_RENAME_TITLE = Slotname
|
||||||
|
PARTICLE_SLOT_ICON = §eSymbol ändern
|
||||||
|
PARTICLE_SLOT_ICON_TITLE = §0Slotsymbol
|
||||||
|
PARTICLE_SLOT_DELETE = §cSlot löschen
|
||||||
|
PARTICLE_SLOT_COST = §7Partikel pro Tick§8: §e{0}§8/§7{1}
|
||||||
|
|
||||||
|
PARTICLE_ENTRY_EDITOR = §0{0}
|
||||||
|
PARTICLE_ENTRY_DESIGN = §7Design§8: {0}
|
||||||
|
PARTICLE_ENTRY_PARTICLE = §7Partikel§8: {0}
|
||||||
|
PARTICLE_ENTRY_LOCKED = §cDesign gesperrt, wird nicht angezeigt
|
||||||
|
PARTICLE_ENTRY_COST = §7Partikel pro Tick§8: §e{0}
|
||||||
|
PARTICLE_ENTRY_COST_SHARE = §7Dieser Eintrag§8: §e{0}
|
||||||
|
PARTICLE_ENTRY_EDIT = §eZum Bearbeiten klicken
|
||||||
|
PARTICLE_ENTRY_DELETE = §cEintrag löschen
|
||||||
|
|
||||||
|
PARTICLE_DESIGN_PICKER = §0Design wählen
|
||||||
|
PARTICLE_CHOICE_PICKER = §0{0}
|
||||||
|
PARTICLE_COLOR_PICKER = §0Farbe wählen
|
||||||
|
|
||||||
|
PARTICLE_VALUE = §7Wert§8: {0}
|
||||||
|
PARTICLE_VALUE_RANDOM = §7Zufällige Farben
|
||||||
|
PARTICLE_VALUE_YES = §aJa
|
||||||
|
PARTICLE_VALUE_NO = §cNein
|
||||||
|
|
||||||
|
PARTICLE_DESIGN_SIMPLE = §fEinfach
|
||||||
|
PARTICLE_DESIGN_CIRCLE = §fKreis
|
||||||
|
PARTICLE_DESIGN_WING = §fFlügel
|
||||||
|
PARTICLE_DESIGN_CLOUD = §fWolke
|
||||||
|
PARTICLE_DESIGN_BURST = §fStoß
|
||||||
|
PARTICLE_DESIGN_FOOTPRINTS = §fFußspuren
|
||||||
|
PARTICLE_DESIGN_TEAM_LINK = §fTeamband
|
||||||
|
PARTICLE_DESIGN_PULSE_SHIMMER = §fSchimmer
|
||||||
|
PARTICLE_DESIGN_HEART_BEAT = §fHerzschlag
|
||||||
|
|
||||||
|
PARTICLE_PARAM_PARTICLE = §fPartikel
|
||||||
|
PARTICLE_PARAM_GLYPH_FLIGHT = §fGlyphenflug
|
||||||
|
PARTICLE_PARAM_BURST_TRIGGER = §fWann
|
||||||
|
PARTICLE_PARAM_BURST_SHAPE = §fForm
|
||||||
|
PARTICLE_PARAM_BURST_RADIUS = §fRingradius
|
||||||
|
PARTICLE_PARAM_BURST_FALL = §fMindestfallhöhe
|
||||||
|
PARTICLE_PARAM_FOOTPRINT_LIFETIME = §fSpurdauer (Ticks)
|
||||||
|
PARTICLE_PARAM_FOOTPRINT_STEP = §fAbstand der Spuren
|
||||||
|
PARTICLE_PARAM_LINK_SPACING = §fAbstand der Partikel
|
||||||
|
PARTICLE_PARAM_PULSE_SPEED = §fPulsdauer (Ticks)
|
||||||
|
PARTICLE_PARAM_PULSE_ROTATION = §fDrehung
|
||||||
|
PARTICLE_PARAM_HEART_BEAT_SPEED = §fSchlagdauer (Ticks)
|
||||||
|
PARTICLE_PARAM_COUNT = §fAnzahl
|
||||||
|
PARTICLE_PARAM_SPREAD_X = §fStreuung X
|
||||||
|
PARTICLE_PARAM_SPREAD_Y = §fStreuung Y
|
||||||
|
PARTICLE_PARAM_SPREAD_Z = §fStreuung Z
|
||||||
|
PARTICLE_PARAM_SPEED = §fGeschwindigkeit
|
||||||
|
PARTICLE_PARAM_COLORS = §fFarben
|
||||||
|
PARTICLE_PARAM_BLOCK = §fBlock
|
||||||
|
PARTICLE_PARAM_TRAIL_DURATION = §fSpurdauer (Ticks)
|
||||||
|
PARTICLE_PARAM_TRAIL_TARGET_X = §fSpurziel X
|
||||||
|
PARTICLE_PARAM_TRAIL_TARGET_Y = §fSpurziel Y
|
||||||
|
PARTICLE_PARAM_TRAIL_TARGET_Z = §fSpurziel Z
|
||||||
|
PARTICLE_PARAM_OFFSET_X = §fVersatz X
|
||||||
|
PARTICLE_PARAM_OFFSET_Y = §fVersatz Y
|
||||||
|
PARTICLE_PARAM_OFFSET_Z = §fVersatz Z
|
||||||
|
PARTICLE_PARAM_INTERVAL = §fAlle n Ticks
|
||||||
|
PARTICLE_PARAM_PLACEMENT = §fAusgerichtet an
|
||||||
|
PARTICLE_PARAM_TICK_MODE = §fAnzeige
|
||||||
|
PARTICLE_PARAM_MOVEMENT = §fBewegung
|
||||||
|
PARTICLE_PARAM_GLIDING = §fElytra
|
||||||
|
PARTICLE_PARAM_GROUND = §fBoden
|
||||||
|
PARTICLE_PARAM_SNEAK = §fDucken
|
||||||
|
PARTICLE_PARAM_VISIBILITY = §fSichtbar für
|
||||||
|
PARTICLE_PARAM_RADIUS = §fRadius
|
||||||
|
PARTICLE_PARAM_CIRCLE_SPEED = §fDrehgeschwindigkeit
|
||||||
|
PARTICLE_PARAM_PHASE = §fStartwinkel
|
||||||
|
PARTICLE_PARAM_RISE = §fAuf und ab Höhe
|
||||||
|
PARTICLE_PARAM_RISE_SPEED = §fAuf und ab Geschwindigkeit
|
||||||
|
PARTICLE_PARAM_BOUNCE = §fZurückspringen
|
||||||
|
PARTICLE_PARAM_INVERTED = §fOben beginnen
|
||||||
|
PARTICLE_PARAM_WING_IMAGE = §fFlügelform
|
||||||
|
PARTICLE_PARAM_WING_SIZE = §fFlügelgröße
|
||||||
|
PARTICLE_PARAM_WING_DETAIL = §fPunkte pro Flügel
|
||||||
|
PARTICLE_PARAM_WING_COLORING = §fFarbmodus
|
||||||
|
PARTICLE_PARAM_WING_SPARKLE = §fFunkeln (sichtbare Punkte, %)
|
||||||
|
PARTICLE_PARAM_WING_MOTION = §fBewegung
|
||||||
|
|
||||||
|
PARTICLE_CHOICE_TICKMODE_MOVE = §fBeim Bewegen
|
||||||
|
PARTICLE_CHOICE_TICKMODE_ALWAYS = §fImmer
|
||||||
|
PARTICLE_CHOICE_PLACEMENT_WORLD = §fWelt (X Osten, Y oben, Z Süden)
|
||||||
|
PARTICLE_CHOICE_PLACEMENT_BODY = §fKörper (X rechts, Y oben, Z vorne)
|
||||||
|
PARTICLE_CHOICE_MOVEMENT_ANY = §fJede Bewegung
|
||||||
|
PARTICLE_CHOICE_MOVEMENT_MOVING = §fNur beim Bewegen
|
||||||
|
PARTICLE_CHOICE_MOVEMENT_STANDING_STILL = §fNur beim Stehen
|
||||||
|
PARTICLE_CHOICE_GLIDING_ANY = §fMit oder ohne Elytra
|
||||||
|
PARTICLE_CHOICE_GLIDING_GLIDING = §fNur beim Gleiten
|
||||||
|
PARTICLE_CHOICE_GLIDING_NOT_GLIDING = §fNicht beim Gleiten
|
||||||
|
PARTICLE_CHOICE_GROUND_ANY = §fAm Boden und in der Luft
|
||||||
|
PARTICLE_CHOICE_GROUND_ON_GROUND = §fNur am Boden
|
||||||
|
PARTICLE_CHOICE_GROUND_IN_AIR = §fNur in der Luft
|
||||||
|
PARTICLE_CHOICE_SNEAK_ANY = §fDucken egal
|
||||||
|
PARTICLE_CHOICE_SNEAK_SNEAKING = §fNur beim Ducken
|
||||||
|
PARTICLE_CHOICE_SNEAK_NOT_SNEAKING = §fNicht beim Ducken
|
||||||
|
PARTICLE_CHOICE_VISIBILITY_EVERYONE = §fAlle
|
||||||
|
PARTICLE_CHOICE_VISIBILITY_ONLY_SELF = §fNur ich
|
||||||
|
PARTICLE_CHOICE_VISIBILITY_ONLY_OTHERS = §fNur andere
|
||||||
|
PARTICLE_CHOICE_IMAGE_SIMPLE = §fEinfach
|
||||||
|
PARTICLE_CHOICE_IMAGE_COMPLEX = §fKomplex
|
||||||
|
PARTICLE_CHOICE_IMAGE_SWORD = §fSchwert
|
||||||
|
PARTICLE_CHOICE_IMAGE_BAT = §fFledermaus
|
||||||
|
PARTICLE_CHOICE_IMAGE_BUTTERFLY = §fSchmetterling
|
||||||
|
PARTICLE_CHOICE_IMAGE_FAIRY = §fFee
|
||||||
|
PARTICLE_CHOICE_IMAGE_MWGL = §fMiniWarGear Liga
|
||||||
|
PARTICLE_CHOICE_IMAGE_SW = §fSteamWar
|
||||||
|
PARTICLE_CHOICE_IMAGE_WGS = §fWarGear Saison
|
||||||
|
PARTICLE_CHOICE_IMAGE_SWORD_CROSSED = §fGekreuzte Schwerter
|
||||||
|
PARTICLE_CHOICE_COLORING_OVER_TIME = §fWechseln mit der Zeit
|
||||||
|
PARTICLE_CHOICE_COLORING_ALONG_WING = §fVom Rücken zu den Spitzen
|
||||||
|
PARTICLE_CHOICE_MOTION_NONE = §fKeine
|
||||||
|
PARTICLE_CHOICE_MOTION_FLAP_SLOW = §fLangsam schlagen
|
||||||
|
PARTICLE_CHOICE_MOTION_FLAP_FAST = §fSchnell schlagen
|
||||||
|
PARTICLE_CHOICE_MOTION_BEAT_ON_JUMP = §fSchlag beim Springen
|
||||||
|
PARTICLE_CHOICE_TRIGGER_JUMP = §fBeim Springen
|
||||||
|
PARTICLE_CHOICE_TRIGGER_LAND = §fBeim Landen
|
||||||
|
PARTICLE_CHOICE_TRIGGER_SNEAK = §fBeim Schleichen
|
||||||
|
PARTICLE_CHOICE_BURSTSHAPE_PUFF = §fWolke
|
||||||
|
PARTICLE_CHOICE_BURSTSHAPE_RING = §fRing
|
||||||
|
PARTICLE_CHOICE_GLYPHFLIGHT_NORMAL = §fHerabschweben
|
||||||
|
PARTICLE_CHOICE_GLYPHFLIGHT_INTO_PLAYER = §fIn den Spieler
|
||||||
|
PARTICLE_CHOICE_GLYPHFLIGHT_OUT_OF_PLAYER = §fAus dem Spieler
|
||||||
|
|
||||||
|
# Particle types
|
||||||
|
PARTICLE_TYPE_DUST = §fStaub
|
||||||
|
PARTICLE_TYPE_DUST_COLOR_TRANSITION = §fFarbverlaufsstaub
|
||||||
|
PARTICLE_TYPE_ENTITY_EFFECT = §fTrankwirbel
|
||||||
|
PARTICLE_TYPE_TINTED_LEAVES = §fGefärbte Blätter
|
||||||
|
PARTICLE_TYPE_BLOCK = §fBlocksplitter
|
||||||
|
PARTICLE_TYPE_BLOCK_CRUMBLE = §fBröckelnder Block
|
||||||
|
PARTICLE_TYPE_BLOCK_MARKER = §fBlockmarkierung
|
||||||
|
PARTICLE_TYPE_DUST_PILLAR = §fStaubsäule
|
||||||
|
PARTICLE_TYPE_FALLING_DUST = §fFallender Staub
|
||||||
|
PARTICLE_TYPE_ITEM = §fGegenstandssplitter
|
||||||
|
PARTICLE_TYPE_TRAIL = §fSpur
|
||||||
|
PARTICLE_TYPE_FLAME = §fFlamme
|
||||||
|
PARTICLE_TYPE_SMALL_FLAME = §fKleine Flamme
|
||||||
|
PARTICLE_TYPE_SOUL_FIRE_FLAME = §fSeelenfeuerflamme
|
||||||
|
PARTICLE_TYPE_SOUL = §fSeele
|
||||||
|
PARTICLE_TYPE_LAVA = §fLavafunken
|
||||||
|
PARTICLE_TYPE_SMOKE = §fRauch
|
||||||
|
PARTICLE_TYPE_LARGE_SMOKE = §fGroßer Rauch
|
||||||
|
PARTICLE_TYPE_WHITE_SMOKE = §fWeißer Rauch
|
||||||
|
PARTICLE_TYPE_CAMPFIRE_COSY_SMOKE = §fLagerfeuerrauch
|
||||||
|
PARTICLE_TYPE_CAMPFIRE_SIGNAL_SMOKE = §fSignalrauch
|
||||||
|
PARTICLE_TYPE_ASH = §fAsche
|
||||||
|
PARTICLE_TYPE_WHITE_ASH = §fWeiße Asche
|
||||||
|
PARTICLE_TYPE_POOF = §fPuff
|
||||||
|
PARTICLE_TYPE_CLOUD = §fWolke
|
||||||
|
PARTICLE_TYPE_SNEEZE = §fNiesen
|
||||||
|
PARTICLE_TYPE_SPIT = §fSpucke
|
||||||
|
PARTICLE_TYPE_EXPLOSION = §fExplosion
|
||||||
|
PARTICLE_TYPE_SONIC_BOOM = §fSchallwelle
|
||||||
|
PARTICLE_TYPE_SWEEP_ATTACK = §fSchwungangriff
|
||||||
|
PARTICLE_TYPE_GUST = §fWindstoß
|
||||||
|
PARTICLE_TYPE_SMALL_GUST = §fKleiner Windstoß
|
||||||
|
PARTICLE_TYPE_DRIPPING_WATER = §fTropfendes Wasser
|
||||||
|
PARTICLE_TYPE_FALLING_WATER = §fFallendes Wasser
|
||||||
|
PARTICLE_TYPE_DRIPPING_LAVA = §fTropfende Lava
|
||||||
|
PARTICLE_TYPE_FALLING_LAVA = §fFallende Lava
|
||||||
|
PARTICLE_TYPE_LANDING_LAVA = §fLandende Lava
|
||||||
|
PARTICLE_TYPE_DRIPPING_HONEY = §fTropfender Honig
|
||||||
|
PARTICLE_TYPE_FALLING_HONEY = §fFallender Honig
|
||||||
|
PARTICLE_TYPE_LANDING_HONEY = §fLandender Honig
|
||||||
|
PARTICLE_TYPE_FALLING_NECTAR = §fFallender Nektar
|
||||||
|
PARTICLE_TYPE_DRIPPING_OBSIDIAN_TEAR = §fTropfende Obsidianträne
|
||||||
|
PARTICLE_TYPE_FALLING_OBSIDIAN_TEAR = §fFallende Obsidianträne
|
||||||
|
PARTICLE_TYPE_LANDING_OBSIDIAN_TEAR = §fLandende Obsidianträne
|
||||||
|
PARTICLE_TYPE_DRIPPING_DRIPSTONE_WATER = §fTropfsteinwasser
|
||||||
|
PARTICLE_TYPE_FALLING_DRIPSTONE_WATER = §fFallendes Tropfsteinwasser
|
||||||
|
PARTICLE_TYPE_DRIPPING_DRIPSTONE_LAVA = §fTropfsteinlava
|
||||||
|
PARTICLE_TYPE_FALLING_DRIPSTONE_LAVA = §fFallende Tropfsteinlava
|
||||||
|
PARTICLE_TYPE_RAIN = §fRegen
|
||||||
|
PARTICLE_TYPE_SPLASH = §fSpritzer
|
||||||
|
PARTICLE_TYPE_FISHING = §fAngelspur
|
||||||
|
PARTICLE_TYPE_UNDERWATER = §fUnterwasser
|
||||||
|
PARTICLE_TYPE_BUBBLE_POP = §fPlatzende Blase
|
||||||
|
PARTICLE_TYPE_DOLPHIN = §fDelfinspur
|
||||||
|
PARTICLE_TYPE_NAUTILUS = §fNautilus
|
||||||
|
PARTICLE_TYPE_SQUID_INK = §fTintenfischtinte
|
||||||
|
PARTICLE_TYPE_GLOW_SQUID_INK = §fLeuchttintenfischtinte
|
||||||
|
PARTICLE_TYPE_GLOW = §fLeuchten
|
||||||
|
PARTICLE_TYPE_FALLING_SPORE_BLOSSOM = §fFallende Sporenblüte
|
||||||
|
PARTICLE_TYPE_SPORE_BLOSSOM_AIR = §fSchwebende Sporen
|
||||||
|
PARTICLE_TYPE_CHERRY_LEAVES = §fKirschblätter
|
||||||
|
PARTICLE_TYPE_PALE_OAK_LEAVES = §fBlasseichenblätter
|
||||||
|
PARTICLE_TYPE_CRIMSON_SPORE = §fKarmesinsporen
|
||||||
|
PARTICLE_TYPE_WARPED_SPORE = §fWirrsporen
|
||||||
|
PARTICLE_TYPE_MYCELIUM = §fMyzel
|
||||||
|
PARTICLE_TYPE_COMPOSTER = §fKomposter
|
||||||
|
PARTICLE_TYPE_FIREFLY = §fGlühwürmchen
|
||||||
|
PARTICLE_TYPE_SNOWFLAKE = §fSchneeflocke
|
||||||
|
PARTICLE_TYPE_ITEM_SNOWBALL = §fSchneeball
|
||||||
|
PARTICLE_TYPE_ITEM_SLIME = §fSchleim
|
||||||
|
PARTICLE_TYPE_ITEM_COBWEB = §fSpinnennetz
|
||||||
|
PARTICLE_TYPE_EGG_CRACK = §fEierschale
|
||||||
|
PARTICLE_TYPE_DUST_PLUME = §fStaubwolke
|
||||||
|
PARTICLE_TYPE_INFESTED = §fBefallen
|
||||||
|
PARTICLE_TYPE_HEART = §fHerz
|
||||||
|
PARTICLE_TYPE_NOTE = §fNote
|
||||||
|
PARTICLE_TYPE_HAPPY_VILLAGER = §fFröhlicher Dorfbewohner
|
||||||
|
PARTICLE_TYPE_ANGRY_VILLAGER = §fWütender Dorfbewohner
|
||||||
|
PARTICLE_TYPE_DAMAGE_INDICATOR = §fSchaden
|
||||||
|
PARTICLE_TYPE_CRIT = §fVolltreffer
|
||||||
|
PARTICLE_TYPE_ENCHANTED_HIT = §fVerzauberter Treffer
|
||||||
|
PARTICLE_TYPE_ENCHANT = §fZauberzeichen
|
||||||
|
PARTICLE_TYPE_EFFECT = §fTrankeffekt
|
||||||
|
PARTICLE_TYPE_INSTANT_EFFECT = §fSoforteffekt
|
||||||
|
PARTICLE_TYPE_WITCH = §fHexenmagie
|
||||||
|
PARTICLE_TYPE_END_ROD = §fEndstab
|
||||||
|
PARTICLE_TYPE_PORTAL = §fPortal
|
||||||
|
PARTICLE_TYPE_REVERSE_PORTAL = §fUmgekehrtes Portal
|
||||||
|
PARTICLE_TYPE_DRAGON_BREATH = §fDrachenatem
|
||||||
|
PARTICLE_TYPE_FIREWORK = §fFeuerwerksfunke
|
||||||
|
PARTICLE_TYPE_TOTEM_OF_UNDYING = §fTotem
|
||||||
|
PARTICLE_TYPE_ELECTRIC_SPARK = §fElektrischer Funke
|
||||||
|
PARTICLE_TYPE_SCRAPE = §fAbkratzen
|
||||||
|
PARTICLE_TYPE_WAX_ON = §fWachsen
|
||||||
|
PARTICLE_TYPE_WAX_OFF = §fEntwachsen
|
||||||
|
PARTICLE_TYPE_SCULK_SOUL = §fSculk-Seele
|
||||||
|
PARTICLE_TYPE_SCULK_CHARGE = §fSculk-Ladung
|
||||||
|
PARTICLE_TYPE_SCULK_CHARGE_POP = §fSculk-Entladung
|
||||||
|
PARTICLE_TYPE_SHRIEK = §fKreischen
|
||||||
|
PARTICLE_TYPE_RAID_OMEN = §fÜberfall-Omen
|
||||||
|
PARTICLE_TYPE_TRIAL_OMEN = §fPrüfungs-Omen
|
||||||
|
PARTICLE_TYPE_OMINOUS_SPAWNING = §fUnheilvolles Erscheinen
|
||||||
|
PARTICLE_TYPE_TRIAL_SPAWNER_DETECTION = §fPrüfungsspawner-Flamme
|
||||||
|
PARTICLE_TYPE_TRIAL_SPAWNER_DETECTION_OMINOUS = §fUnheilvolle Prüfungsspawner-Flamme
|
||||||
|
PARTICLE_TYPE_VAULT_CONNECTION = §fTresorverbindung
|
||||||
|
|
||||||
|
# Particle blocks
|
||||||
|
PARTICLE_CHOICE_BLOCK_STONE = §fStein
|
||||||
|
PARTICLE_CHOICE_BLOCK_COBBLESTONE = §fBruchstein
|
||||||
|
PARTICLE_CHOICE_BLOCK_DIRT = §fErde
|
||||||
|
PARTICLE_CHOICE_BLOCK_GRASS_BLOCK = §fGrasblock
|
||||||
|
PARTICLE_CHOICE_BLOCK_SAND = §fSand
|
||||||
|
PARTICLE_CHOICE_BLOCK_RED_SAND = §fRoter Sand
|
||||||
|
PARTICLE_CHOICE_BLOCK_GRAVEL = §fKies
|
||||||
|
PARTICLE_CHOICE_BLOCK_OAK_PLANKS = §fEichenholzbretter
|
||||||
|
PARTICLE_CHOICE_BLOCK_OAK_LEAVES = §fEichenlaub
|
||||||
|
PARTICLE_CHOICE_BLOCK_CHERRY_LEAVES = §fKirschlaub
|
||||||
|
PARTICLE_CHOICE_BLOCK_SNOW_BLOCK = §fSchneeblock
|
||||||
|
PARTICLE_CHOICE_BLOCK_ICE = §fEis
|
||||||
|
PARTICLE_CHOICE_BLOCK_GLASS = §fGlas
|
||||||
|
PARTICLE_CHOICE_BLOCK_TNT = §fTNT
|
||||||
|
PARTICLE_CHOICE_BLOCK_PUMPKIN = §fKürbis
|
||||||
|
PARTICLE_CHOICE_BLOCK_MELON = §fMelone
|
||||||
|
PARTICLE_CHOICE_BLOCK_HONEY_BLOCK = §fHonigblock
|
||||||
|
PARTICLE_CHOICE_BLOCK_SLIME_BLOCK = §fSchleimblock
|
||||||
|
PARTICLE_CHOICE_BLOCK_NETHERRACK = §fNetherrack
|
||||||
|
PARTICLE_CHOICE_BLOCK_SOUL_SAND = §fSeelensand
|
||||||
|
PARTICLE_CHOICE_BLOCK_MAGMA_BLOCK = §fMagmablock
|
||||||
|
PARTICLE_CHOICE_BLOCK_END_STONE = §fEndstein
|
||||||
|
PARTICLE_CHOICE_BLOCK_OBSIDIAN = §fObsidian
|
||||||
|
PARTICLE_CHOICE_BLOCK_PRISMARINE = §fPrismarin
|
||||||
|
PARTICLE_CHOICE_BLOCK_SCULK = §fSculk
|
||||||
|
PARTICLE_CHOICE_BLOCK_AMETHYST_BLOCK = §fAmethystblock
|
||||||
|
PARTICLE_CHOICE_BLOCK_COAL_BLOCK = §fKohleblock
|
||||||
|
PARTICLE_CHOICE_BLOCK_IRON_BLOCK = §fEisenblock
|
||||||
|
PARTICLE_CHOICE_BLOCK_GOLD_BLOCK = §fGoldblock
|
||||||
|
PARTICLE_CHOICE_BLOCK_REDSTONE_BLOCK = §fRedstone-Block
|
||||||
|
PARTICLE_CHOICE_BLOCK_LAPIS_BLOCK = §fLapislazuliblock
|
||||||
|
PARTICLE_CHOICE_BLOCK_EMERALD_BLOCK = §fSmaragdblock
|
||||||
|
PARTICLE_CHOICE_BLOCK_DIAMOND_BLOCK = §fDiamantblock
|
||||||
|
PARTICLE_CHOICE_BLOCK_WHITE_WOOL = §fWeiße Wolle
|
||||||
|
PARTICLE_CHOICE_BLOCK_RED_WOOL = §fRote Wolle
|
||||||
|
PARTICLE_CHOICE_BLOCK_ORANGE_WOOL = §fOrange Wolle
|
||||||
|
PARTICLE_CHOICE_BLOCK_YELLOW_WOOL = §fGelbe Wolle
|
||||||
|
PARTICLE_CHOICE_BLOCK_LIME_WOOL = §fHellgrüne Wolle
|
||||||
|
PARTICLE_CHOICE_BLOCK_LIGHT_BLUE_WOOL = §fHellblaue Wolle
|
||||||
|
PARTICLE_CHOICE_BLOCK_BLUE_WOOL = §fBlaue Wolle
|
||||||
|
PARTICLE_CHOICE_BLOCK_PURPLE_WOOL = §fViolette Wolle
|
||||||
|
PARTICLE_CHOICE_BLOCK_PINK_WOOL = §fRosa Wolle
|
||||||
|
PARTICLE_CHOICE_BLOCK_BLACK_WOOL = §fSchwarze Wolle
|
||||||
|
|
||||||
PARTICLE_PLAYER_1063 = §7Kreisende Kreise
|
PARTICLE_PLAYER_1063 = §7Kreisende Kreise
|
||||||
PARTICLE_PLAYER_10697_AURA = §7Rauchgranate
|
PARTICLE_PLAYER_10697_AURA = §7Rauchgranate
|
||||||
|
|
||||||
PARTICLE_TEAM_PULSE_HEART_BEAT = §cHerzschlag
|
PARTICLE_TEAM_PULSE_HEART_BEAT = §cHerzschlag
|
||||||
|
|
||||||
PARTICLE_RAINCLOUD_NORMAL = §fRegenwolke
|
|
||||||
PARTICLE_RAINCLOUD_RED = §fLavawolke
|
|
||||||
PARTICLE_RAINCLOUD_YELLOW = §fNektarwolke
|
|
||||||
PARTICLE_RAINCLOUD_ORANGE = §fHonigwolke
|
|
||||||
PARTICLE_RAINCLOUD_GREEN = §fSporenwolke
|
|
||||||
PARTICLE_RAINCLOUD_PURPLE = §fObsidanwolke
|
|
||||||
PARTICLE_RAINCLOUD_NAUTILUS = §fNautiluswolke
|
|
||||||
PARTICLE_RAINCLOUD_ENCHANTMENT = §fVerzauberungswolke
|
|
||||||
PARTICLE_RAINCLOUD_SLIME = §fSchleimwolke
|
|
||||||
PARTICLE_RAINCLOUD_HAIL = §fHagelwolke
|
|
||||||
PARTICLE_RAINCLOUD_SNOW = §fSchneewolke
|
|
||||||
PARTICLE_RAINCLOUD_TOTEM = §fTotemwolke
|
|
||||||
|
|
||||||
PARTICLE_EVENT_ENCHANTING = §cVerzaubert
|
PARTICLE_EVENT_ENCHANTING = §cVerzaubert
|
||||||
PARTICLE_EVENT_CLOUD = §fWolken
|
|
||||||
PARTICLE_EVENT_SMOKE = §7Rauch
|
|
||||||
PARTICLE_EVENT_WATER = §bWasser
|
|
||||||
PARTICLE_EVENT_WINGS = §fFlügel
|
|
||||||
PARTICLE_EVENT_RAIN_CLOUD = §fRegenwolke
|
PARTICLE_EVENT_RAIN_CLOUD = §fRegenwolke
|
||||||
PARTICLE_EVENT_WGS = §fWGS
|
|
||||||
PARTICLE_EVENT_WARGEARCLASH = §fClash
|
|
||||||
|
|
||||||
PARTICLE_EGG_HUNT_EASY = §aGrünplfanzen
|
|
||||||
PARTICLE_EGG_HUNT_MEDIUM = §eVersteckspiel
|
PARTICLE_EGG_HUNT_MEDIUM = §eVersteckspiel
|
||||||
PARTICLE_EGG_HUNT_HARD = §cOrbit
|
PARTICLE_EGG_HUNT_HARD = §cOrbit
|
||||||
PARTICLE_EGG_HUNT_EXTREME = §5Lila regen
|
|
||||||
PARTICLE_EGG_HUNT_ADVANCED = §5Abheben
|
PARTICLE_EGG_HUNT_ADVANCED = §5Abheben
|
||||||
PARTICLE_EGG_HUNT_HALF = §fDie Jagd ist eröffnet
|
|
||||||
|
|
||||||
JUMP_AND_RUN_FINISHED = §aBeendet in {0} mit {1} Fails
|
JUMP_AND_RUN_FINISHED = §aBeendet in {0} mit {1} Fails
|
||||||
JUMP_AND_RUN_PERSONAL_BEST = §aNice! Du hast deinen Rekord um {0} verbessert!
|
JUMP_AND_RUN_PERSONAL_BEST = §aNice! Du hast deinen Rekord um {0} verbessert!
|
||||||
|
|||||||
@@ -0,0 +1,95 @@
|
|||||||
|
/*
|
||||||
|
* This file is a part of the SteamWar software.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2026 SteamWar.de-Serverteam
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program 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 Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.steamwar.lobby.particle;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
import org.bukkit.Location;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
import org.bukkit.entity.Pose;
|
||||||
|
import org.bukkit.util.Vector;
|
||||||
|
|
||||||
|
// The player's body as a coordinate system: origin at the feet, up towards the head, forward out of the chest.
|
||||||
|
// Standing it turns with the body (as BodyMotion tracks it); gliding or swimming the body lies along the look direction.
|
||||||
|
@Getter
|
||||||
|
public class BodyFrame {
|
||||||
|
|
||||||
|
private static final Vector WORLD_UP = new Vector(0, 1, 0);
|
||||||
|
private static final double HALF_BODY = 0.9;
|
||||||
|
|
||||||
|
private final Location origin;
|
||||||
|
private final Vector right;
|
||||||
|
private final Vector up;
|
||||||
|
private final Vector forward;
|
||||||
|
// Reference directions of rings around the body; they do not turn with the body
|
||||||
|
private final Vector ringStart;
|
||||||
|
private final Vector ringSide;
|
||||||
|
|
||||||
|
private BodyFrame(Location origin, Vector right, Vector up, Vector forward) {
|
||||||
|
this.origin = origin;
|
||||||
|
this.right = right;
|
||||||
|
this.up = up;
|
||||||
|
this.forward = forward;
|
||||||
|
|
||||||
|
// World east projected onto the plane around the body, so a standing ring matches the world ring
|
||||||
|
Vector start = new Vector(1, 0, 0).subtract(up.clone().multiply(up.getX()));
|
||||||
|
if (start.lengthSquared() < 1e-6) {
|
||||||
|
start = new Vector(0, 0, 1).subtract(up.clone().multiply(up.getZ()));
|
||||||
|
}
|
||||||
|
this.ringStart = start.normalize();
|
||||||
|
this.ringSide = up.clone().crossProduct(ringStart);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static BodyFrame of(Player player) {
|
||||||
|
Location location = player.getLocation();
|
||||||
|
Pose pose = player.getPose();
|
||||||
|
if (pose == Pose.FALL_FLYING || pose == Pose.SWIMMING) {
|
||||||
|
Vector up = location.getDirection().normalize();
|
||||||
|
Vector right = up.clone().crossProduct(WORLD_UP);
|
||||||
|
if (right.lengthSquared() < 1e-6) {
|
||||||
|
right = horizontal(BodyMotion.yaw(player)).crossProduct(WORLD_UP);
|
||||||
|
}
|
||||||
|
right.normalize();
|
||||||
|
Vector forward = up.clone().crossProduct(right).normalize();
|
||||||
|
// The lying body is centred in its short hitbox, its feet lie behind the centre
|
||||||
|
Location origin = location.clone().add(0, player.getHeight() / 2, 0).subtract(up.clone().multiply(HALF_BODY));
|
||||||
|
return new BodyFrame(origin, right, up, forward);
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector forward = horizontal(BodyMotion.yaw(player));
|
||||||
|
Vector right = forward.clone().crossProduct(WORLD_UP).normalize();
|
||||||
|
return new BodyFrame(location, right, WORLD_UP.clone(), forward);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Vector horizontal(float yaw) {
|
||||||
|
double radians = Math.toRadians(yaw);
|
||||||
|
return new Vector(-Math.sin(radians), 0, Math.cos(radians));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Vector local(double right, double up, double forward) {
|
||||||
|
return this.right.clone().multiply(right)
|
||||||
|
.add(this.up.clone().multiply(up))
|
||||||
|
.add(this.forward.clone().multiply(forward));
|
||||||
|
}
|
||||||
|
|
||||||
|
// A point on a ring around the up axis; angle 0 and the turning direction match the world rings
|
||||||
|
public Vector ring(double distance, double angle) {
|
||||||
|
return ringStart.clone().multiply(Math.cos(angle) * distance).add(ringSide.clone().multiply(Math.sin(angle) * distance));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,165 @@
|
|||||||
|
/*
|
||||||
|
* This file is a part of the SteamWar software.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2026 SteamWar.de-Serverteam
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program 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 Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.steamwar.lobby.particle;
|
||||||
|
|
||||||
|
import lombok.experimental.UtilityClass;
|
||||||
|
import org.bukkit.Location;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.WeakHashMap;
|
||||||
|
|
||||||
|
// How a player's body moves: the direction it faces, worked out the way the Minecraft client turns the body, because
|
||||||
|
// the server's own body rotation for players does not follow what other players see, and how fast it moves.
|
||||||
|
// Walking turns the body towards the walking direction; standing still it only turns once the head looks more than
|
||||||
|
// 50 degrees to the side. It also notices jumps, landings and starting to sneak, which reward touches react to.
|
||||||
|
@UtilityClass
|
||||||
|
public class BodyMotion {
|
||||||
|
|
||||||
|
private final float MAX_HEAD_TURN = 50;
|
||||||
|
private final double MOVING = 0.0025;
|
||||||
|
|
||||||
|
private final Map<Player, State> states = new WeakHashMap<>();
|
||||||
|
// Counts server ticks, so events can be matched to the tick they happened in
|
||||||
|
private long tick;
|
||||||
|
|
||||||
|
private static class State {
|
||||||
|
private float body;
|
||||||
|
private double x;
|
||||||
|
private double y;
|
||||||
|
private double z;
|
||||||
|
// Blocks per tick, smoothed so single steps do not jerk the wings
|
||||||
|
private double speed;
|
||||||
|
private boolean onGround = true;
|
||||||
|
private boolean sneaking;
|
||||||
|
// Highest point of the current time in the air
|
||||||
|
private double peakY;
|
||||||
|
private long jumpTick = Long.MIN_VALUE;
|
||||||
|
private long landTick = Long.MIN_VALUE;
|
||||||
|
private double landFall;
|
||||||
|
private long sneakTick = Long.MIN_VALUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Called once per server tick before the players are updated
|
||||||
|
public void nextTick() {
|
||||||
|
tick++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Called once per server tick for every online player
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
|
public void tick(Player player) {
|
||||||
|
Location location = player.getLocation();
|
||||||
|
State state = states.get(player);
|
||||||
|
if (state == null) {
|
||||||
|
state = new State();
|
||||||
|
state.body = location.getYaw();
|
||||||
|
state.x = location.getX();
|
||||||
|
state.y = location.getY();
|
||||||
|
state.z = location.getZ();
|
||||||
|
states.put(player, state);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
float head = location.getYaw();
|
||||||
|
double dx = location.getX() - state.x;
|
||||||
|
double dy = location.getY() - state.y;
|
||||||
|
double dz = location.getZ() - state.z;
|
||||||
|
state.x = location.getX();
|
||||||
|
state.y = location.getY();
|
||||||
|
state.z = location.getZ();
|
||||||
|
state.speed = state.speed * 0.8 + Math.sqrt(dx * dx + dy * dy + dz * dz) * 0.2;
|
||||||
|
|
||||||
|
boolean onGround = player.isOnGround();
|
||||||
|
if (state.onGround && !onGround && dy > 0) {
|
||||||
|
state.jumpTick = tick;
|
||||||
|
}
|
||||||
|
if (!onGround) {
|
||||||
|
state.peakY = state.onGround ? location.getY() : Math.max(state.peakY, location.getY());
|
||||||
|
} else if (!state.onGround) {
|
||||||
|
state.landTick = tick;
|
||||||
|
state.landFall = state.peakY - location.getY();
|
||||||
|
}
|
||||||
|
state.onGround = onGround;
|
||||||
|
if (player.isSneaking() && !state.sneaking) {
|
||||||
|
state.sneakTick = tick;
|
||||||
|
}
|
||||||
|
state.sneaking = player.isSneaking();
|
||||||
|
|
||||||
|
float target = state.body;
|
||||||
|
if (dx * dx + dz * dz > MOVING) {
|
||||||
|
target = (float) Math.toDegrees(Math.atan2(dz, dx)) - 90;
|
||||||
|
// Walking backwards keeps the body facing forwards
|
||||||
|
float away = Math.abs(wrap(head) - target);
|
||||||
|
if (away > 95 && away < 265) {
|
||||||
|
target -= 180;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
state.body += wrap(target - state.body) * 0.3f;
|
||||||
|
float headTurn = wrap(head - state.body);
|
||||||
|
if (Math.abs(headTurn) > MAX_HEAD_TURN) {
|
||||||
|
state.body += headTurn - Math.signum(headTurn) * MAX_HEAD_TURN;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// The tracked body direction in degrees, or the head direction for players not tracked yet
|
||||||
|
public float yaw(Player player) {
|
||||||
|
State state = states.get(player);
|
||||||
|
return state == null ? player.getLocation().getYaw() : state.body;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Smoothed speed in blocks per tick; sprinting is about 0.28, fast elytra flight well above 1
|
||||||
|
public double speed(Player player) {
|
||||||
|
State state = states.get(player);
|
||||||
|
return state == null ? 0 : state.speed;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long currentTick() {
|
||||||
|
return tick;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean jumped(Player player) {
|
||||||
|
State state = states.get(player);
|
||||||
|
return state != null && state.jumpTick == tick;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ticks since the last jump, or a large number if there was none
|
||||||
|
public long ticksSinceJump(Player player) {
|
||||||
|
State state = states.get(player);
|
||||||
|
return state == null || state.jumpTick == Long.MIN_VALUE ? Long.MAX_VALUE : tick - state.jumpTick;
|
||||||
|
}
|
||||||
|
|
||||||
|
// How far the player fell if they landed this tick, otherwise -1
|
||||||
|
public double landed(Player player) {
|
||||||
|
State state = states.get(player);
|
||||||
|
return state != null && state.landTick == tick ? state.landFall : -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean startedSneaking(Player player) {
|
||||||
|
State state = states.get(player);
|
||||||
|
return state != null && state.sneakTick == tick;
|
||||||
|
}
|
||||||
|
|
||||||
|
private float wrap(float degrees) {
|
||||||
|
float wrapped = degrees % 360;
|
||||||
|
if (wrapped >= 180) wrapped -= 360;
|
||||||
|
if (wrapped < -180) wrapped += 360;
|
||||||
|
return wrapped;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,75 @@
|
|||||||
|
/*
|
||||||
|
* This file is a part of the SteamWar software.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2026 SteamWar.de-Serverteam
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program 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 Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.steamwar.lobby.particle;
|
||||||
|
|
||||||
|
import de.steamwar.lobby.particle.slot.ParticleEntry;
|
||||||
|
import de.steamwar.lobby.particle.slot.ParticleSet;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class CompiledSet {
|
||||||
|
|
||||||
|
private final List<ParticleElement> always = new ArrayList<>();
|
||||||
|
private final List<ParticleElement> move = new ArrayList<>();
|
||||||
|
|
||||||
|
public static CompiledSet of(ParticleElement... particleElements) {
|
||||||
|
CompiledSet compiledSet = new CompiledSet();
|
||||||
|
for (ParticleElement particleElement : particleElements) {
|
||||||
|
compiledSet.add(particleElement);
|
||||||
|
}
|
||||||
|
return compiledSet;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Entries whose design or particle type is not unlocked are skipped, not removed
|
||||||
|
public static CompiledSet of(ParticleSet particleSet, UnlockContext context) {
|
||||||
|
CompiledSet compiledSet = new CompiledSet();
|
||||||
|
for (ParticleEntry entry : particleSet.getEntries()) {
|
||||||
|
if (entry.isUnlocked(context)) {
|
||||||
|
compiledSet.add(entry.build());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return compiledSet;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void add(ParticleElement particleElement) {
|
||||||
|
if (particleElement.tickType() == ParticleTickType.ALWAYS) {
|
||||||
|
always.add(particleElement);
|
||||||
|
} else {
|
||||||
|
move.add(particleElement);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasAlways() {
|
||||||
|
return !always.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasMove() {
|
||||||
|
return !move.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void tickAlways(ParticleTickData particleTickData) {
|
||||||
|
always.forEach(particleElement -> particleElement.tick(particleTickData));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void tickMove(ParticleTickData particleTickData) {
|
||||||
|
move.forEach(particleElement -> particleElement.tick(particleTickData));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,160 @@
|
|||||||
|
/*
|
||||||
|
* This file is a part of the SteamWar software.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2026 SteamWar.de-Serverteam
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program 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 Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.steamwar.lobby.particle;
|
||||||
|
|
||||||
|
import org.bukkit.util.Vector;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
// Wing shapes drawn from lines and curves instead of images, so their number of points can be chosen.
|
||||||
|
// Coordinates use the same units as the wing images: x is sideways from the spine, y upwards, about 11 by 13.
|
||||||
|
public enum DrawnWings {
|
||||||
|
|
||||||
|
// An arm bone along the top, fingers spreading from the wrist and skin hanging between them
|
||||||
|
BAT {
|
||||||
|
@Override
|
||||||
|
List<List<Vector>> rightWing() {
|
||||||
|
List<List<Vector>> lines = new ArrayList<>();
|
||||||
|
lines.add(curve(p(0.5, 7), p(3, 9.5), p(6, 11), p(11, 12.5)));
|
||||||
|
lines.add(curve(p(6, 11), p(7, 4)));
|
||||||
|
lines.add(curve(p(6, 11), p(9, 6)));
|
||||||
|
lines.add(curve(p(6, 11), p(10.8, 9)));
|
||||||
|
lines.add(curve(p(0.5, 5), p(3.5, 4.6), p(5.5, 3.4), p(7, 4), p(7.6, 5.4), p(9, 6), p(9.6, 7.6), p(10.8, 9), p(11.2, 10.8), p(11, 12.5)));
|
||||||
|
return lines;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// The butterfly curve r = e^cos(t) - 2 cos(4t), which already holds both wings; its small loops around the body are left out
|
||||||
|
BUTTERFLY {
|
||||||
|
@Override
|
||||||
|
List<List<Vector>> rightWing() {
|
||||||
|
List<List<Vector>> lines = new ArrayList<>();
|
||||||
|
List<Vector> current = new ArrayList<>();
|
||||||
|
for (int i = 0; i <= 400; i++) {
|
||||||
|
double t = Math.PI * i / 400;
|
||||||
|
double r = Math.exp(Math.cos(t)) - 2 * Math.cos(4 * t);
|
||||||
|
if (Math.abs(r) < 0.9) {
|
||||||
|
if (current.size() > 1) lines.add(current);
|
||||||
|
current = new ArrayList<>();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
current.add(p(Math.sin(t) * r * 3, Math.cos(t) * r * 2 + 6));
|
||||||
|
}
|
||||||
|
if (current.size() > 1) lines.add(current);
|
||||||
|
return lines;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// Two thin rounded pairs, a long one above and a short one below
|
||||||
|
FAIRY {
|
||||||
|
@Override
|
||||||
|
List<List<Vector>> rightWing() {
|
||||||
|
return List.of(ellipse(5.2, 9.3, 5, 1.9, 35), ellipse(3.8, 4.6, 3.6, 1.5, -30));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
;
|
||||||
|
|
||||||
|
// The right wing as lines; the left wing mirrors it
|
||||||
|
abstract List<List<Vector>> rightWing();
|
||||||
|
|
||||||
|
private static final Map<String, WingDesign> CACHE = new HashMap<>();
|
||||||
|
// The shapes are drawn with the shoulder joint at y = 7; moving it to 3.3 puts it at the shoulder blades
|
||||||
|
// (about 1.3 blocks above the feet at the default size) instead of at head height
|
||||||
|
private static final double SHOULDER_SHIFT = 7 - 3.3;
|
||||||
|
|
||||||
|
// Both wings with the given number of points per wing, spread evenly along the lines
|
||||||
|
public WingDesign withDetail(int pointsPerWing) {
|
||||||
|
return CACHE.computeIfAbsent(name() + "@" + pointsPerWing, key -> {
|
||||||
|
List<Vector> right = sample(rightWing(), pointsPerWing);
|
||||||
|
right.forEach(point -> point.setY(point.getY() - SHOULDER_SHIFT));
|
||||||
|
List<Vector> both = new ArrayList<>(right);
|
||||||
|
right.forEach(point -> both.add(new Vector(-point.getX(), point.getY(), 0)));
|
||||||
|
return new WingDesign.WingDesignImpl(both.toArray(new Vector[0]));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<Vector> sample(List<List<Vector>> lines, int count) {
|
||||||
|
double total = lines.stream().mapToDouble(DrawnWings::length).sum();
|
||||||
|
List<Vector> points = new ArrayList<>();
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
double distance = total * (i + 0.5) / count;
|
||||||
|
for (List<Vector> line : lines) {
|
||||||
|
double length = length(line);
|
||||||
|
if (distance <= length) {
|
||||||
|
points.add(at(line, distance));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
distance -= length;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return points;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static double length(List<Vector> line) {
|
||||||
|
double length = 0;
|
||||||
|
for (int i = 1; i < line.size(); i++) length += line.get(i).distance(line.get(i - 1));
|
||||||
|
return length;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Vector at(List<Vector> line, double distance) {
|
||||||
|
for (int i = 1; i < line.size(); i++) {
|
||||||
|
double segment = line.get(i).distance(line.get(i - 1));
|
||||||
|
if (distance <= segment) {
|
||||||
|
return line.get(i - 1).clone().add(line.get(i).clone().subtract(line.get(i - 1)).multiply(segment == 0 ? 0 : distance / segment));
|
||||||
|
}
|
||||||
|
distance -= segment;
|
||||||
|
}
|
||||||
|
return line.get(line.size() - 1).clone();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Vector p(double x, double y) {
|
||||||
|
return new Vector(x, y, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// A smooth curve through the given points (Catmull-Rom), as many short segments
|
||||||
|
private static List<Vector> curve(Vector... points) {
|
||||||
|
List<Vector> result = new ArrayList<>();
|
||||||
|
for (int i = 0; i < points.length - 1; i++) {
|
||||||
|
Vector p0 = points[Math.max(0, i - 1)], p1 = points[i], p2 = points[i + 1], p3 = points[Math.min(points.length - 1, i + 2)];
|
||||||
|
for (int step = 0; step < 16; step++) {
|
||||||
|
double t = step / 16.0, t2 = t * t, t3 = t2 * t;
|
||||||
|
result.add(p1.clone().multiply(2)
|
||||||
|
.add(p2.clone().subtract(p0).multiply(t))
|
||||||
|
.add(p0.clone().multiply(2).subtract(p1.clone().multiply(5)).add(p2.clone().multiply(4)).subtract(p3).multiply(t2))
|
||||||
|
.add(p1.clone().multiply(3).subtract(p0).subtract(p2.clone().multiply(3)).add(p3).multiply(t3))
|
||||||
|
.multiply(0.5));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result.add(points[points.length - 1].clone());
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<Vector> ellipse(double centerX, double centerY, double radiusX, double radiusY, double tiltDegrees) {
|
||||||
|
double tilt = Math.toRadians(tiltDegrees);
|
||||||
|
List<Vector> result = new ArrayList<>();
|
||||||
|
for (int i = 0; i <= 64; i++) {
|
||||||
|
double a = 2 * Math.PI * i / 64;
|
||||||
|
double x = Math.cos(a) * radiusX, y = Math.sin(a) * radiusY;
|
||||||
|
result.add(p(centerX + x * Math.cos(tilt) - y * Math.sin(tilt), centerY + x * Math.sin(tilt) + y * Math.cos(tilt)));
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,96 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is a part of the SteamWar software.
|
|
||||||
*
|
|
||||||
* Copyright (C) 2025 SteamWar.de-Serverteam
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU Affero General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program 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 Affero General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package de.steamwar.lobby.particle;
|
|
||||||
|
|
||||||
import de.steamwar.inventory.SWItem;
|
|
||||||
import de.steamwar.lobby.LobbySystem;
|
|
||||||
import de.steamwar.sql.SteamwarUser;
|
|
||||||
import lombok.Getter;
|
|
||||||
import org.bukkit.Material;
|
|
||||||
import org.bukkit.entity.Player;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
public class ParticleData {
|
|
||||||
|
|
||||||
private final Material material;
|
|
||||||
private final String name;
|
|
||||||
private final List<String> attributes = new ArrayList<>();
|
|
||||||
|
|
||||||
@Getter
|
|
||||||
private final ParticleRequirement requirement;
|
|
||||||
|
|
||||||
@Getter
|
|
||||||
private final ParticleElement particleElement;
|
|
||||||
|
|
||||||
public ParticleData(Material material, String name, ParticleElement particleElement) {
|
|
||||||
this.material = material;
|
|
||||||
this.name = name;
|
|
||||||
this.requirement = ParticleRequirement.NO_REQUIRMENT;
|
|
||||||
this.particleElement = particleElement;
|
|
||||||
particleElement.aggregateAttributes(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
public ParticleData(Material material, String name, ParticleRequirement requirement, ParticleElement particleElement) {
|
|
||||||
this.material = material;
|
|
||||||
this.name = name;
|
|
||||||
this.requirement = requirement;
|
|
||||||
this.particleElement = particleElement;
|
|
||||||
particleElement.aggregateAttributes(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
public ParticleData add(String attribute) {
|
|
||||||
attributes.add(attribute);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public SWItem toSWItem(Player player, SteamwarUser user, Set<Integer> eventTeilnahme, String eggHuntConfig) {
|
|
||||||
String translatedName;
|
|
||||||
try {
|
|
||||||
translatedName = LobbySystem.getMessage().parse(name, player);
|
|
||||||
} catch (Exception e) {
|
|
||||||
translatedName = name;
|
|
||||||
}
|
|
||||||
|
|
||||||
List<String> lore = new ArrayList<>();
|
|
||||||
lore.add("");
|
|
||||||
if (!attributes.isEmpty()) {
|
|
||||||
lore.add(LobbySystem.getMessage().parse("PARTICLE_ATTRIBUTE", player));
|
|
||||||
attributes.forEach(attribute -> lore.add(LobbySystem.getMessage().parse(attribute, player)));
|
|
||||||
lore.add("");
|
|
||||||
}
|
|
||||||
|
|
||||||
String unlockedBy = requirement.getRequirementName(player);
|
|
||||||
if (unlockedBy != null) {
|
|
||||||
lore.add(LobbySystem.getMessage().parse("PARTICLE_UNLOCKED_BY", player));
|
|
||||||
lore.add(unlockedBy);
|
|
||||||
lore.add("");
|
|
||||||
}
|
|
||||||
if (requirement.test(user, eventTeilnahme, eggHuntConfig)) {
|
|
||||||
lore.add(LobbySystem.getMessage().parse("PARTICLE_SELECT", player));
|
|
||||||
} else {
|
|
||||||
lore.add(LobbySystem.getMessage().parse("PARTICLE_LOCKED", player));
|
|
||||||
}
|
|
||||||
return new SWItem(material, translatedName, lore, false, clickType -> {
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -19,9 +19,7 @@
|
|||||||
|
|
||||||
package de.steamwar.lobby.particle;
|
package de.steamwar.lobby.particle;
|
||||||
|
|
||||||
import org.bukkit.Bukkit;
|
|
||||||
import org.bukkit.Color;
|
import org.bukkit.Color;
|
||||||
import org.bukkit.Location;
|
|
||||||
import org.bukkit.Particle;
|
import org.bukkit.Particle;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
|
|
||||||
@@ -47,23 +45,18 @@ public interface ParticleElement {
|
|||||||
return new Particle.DustOptions(Color.fromRGB(r, g, b), (float) (RANDOM.nextFloat() / 2 + 0.5));
|
return new Particle.DustOptions(Color.fromRGB(r, g, b), (float) (RANDOM.nextFloat() / 2 + 0.5));
|
||||||
}
|
}
|
||||||
|
|
||||||
default void display(Location location, Player root, boolean onlySelf, boolean onlyOther, Consumer<Player> consumer) {
|
default void display(ParticleTickData particleTickData, Consumer<Player> consumer) {
|
||||||
if (onlySelf) {
|
Player root = particleTickData.getPlayer();
|
||||||
|
if (particleTickData.isOnlySelf()) {
|
||||||
consumer.accept(root);
|
consumer.accept(root);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Bukkit.getOnlinePlayers().forEach(player -> {
|
for (Player player : particleTickData.getViewers()) {
|
||||||
if (onlyOther && player == root) {
|
if (particleTickData.isOnlyOthers() && player == root) {
|
||||||
return;
|
continue;
|
||||||
}
|
}
|
||||||
int viewDistance = player.getClientViewDistance() * 16;
|
consumer.accept(player);
|
||||||
if (location.distanceSquared(player.getLocation()) <= viewDistance * viewDistance) {
|
}
|
||||||
consumer.accept(player);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
default void aggregateAttributes(ParticleData particleData) {
|
|
||||||
}
|
}
|
||||||
|
|
||||||
default ParticleTickType tickType() {
|
default ParticleTickType tickType() {
|
||||||
|
|||||||
@@ -1,145 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is a part of the SteamWar software.
|
|
||||||
*
|
|
||||||
* Copyright (C) 2025 SteamWar.de-Serverteam
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU Affero General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program 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 Affero General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package de.steamwar.lobby.particle;
|
|
||||||
|
|
||||||
import de.steamwar.inventory.SWItem;
|
|
||||||
import de.steamwar.inventory.SWListInv;
|
|
||||||
import de.steamwar.lobby.LobbySystem;
|
|
||||||
import de.steamwar.lobby.particle.particles.*;
|
|
||||||
import de.steamwar.lobby.particle.particles.custom.CustomEasterParticle;
|
|
||||||
import de.steamwar.lobby.particle.particles.custom.CustomPlayerParticle;
|
|
||||||
import de.steamwar.lobby.particle.particles.custom.CustomTeamParticle;
|
|
||||||
import de.steamwar.lobby.special.easter.EggHunt;
|
|
||||||
import de.steamwar.lobby.util.LobbyPlayer;
|
|
||||||
import de.steamwar.sql.Event;
|
|
||||||
import de.steamwar.sql.SteamwarUser;
|
|
||||||
import de.steamwar.sql.TeamTeilnahme;
|
|
||||||
import de.steamwar.sql.UserConfig;
|
|
||||||
import lombok.experimental.UtilityClass;
|
|
||||||
import org.bukkit.Material;
|
|
||||||
import org.bukkit.entity.Player;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
@UtilityClass
|
|
||||||
public class ParticleInventory {
|
|
||||||
|
|
||||||
private final Class<?>[] PARTICLES = {
|
|
||||||
PlayerParticle.class,
|
|
||||||
CustomPlayerParticle.class,
|
|
||||||
TeamParticle.class,
|
|
||||||
CustomTeamParticle.class,
|
|
||||||
RainCloudParticle.class,
|
|
||||||
ServerTeamParticle.class,
|
|
||||||
EventParticle.class,
|
|
||||||
EventParticlePlacement.class,
|
|
||||||
EventParticleParticipation.class,
|
|
||||||
EasterParticle.class,
|
|
||||||
CustomEasterParticle.class,
|
|
||||||
};
|
|
||||||
|
|
||||||
public String convertToString(ParticleEnum particleEnum) {
|
|
||||||
return particleEnum.getClass().getSimpleName() + "@" + ((Enum<?>) particleEnum).name();
|
|
||||||
}
|
|
||||||
|
|
||||||
public ParticleEnum convertFromString(String string) {
|
|
||||||
String[] split = string.split("@");
|
|
||||||
Class<?> clazz = null;
|
|
||||||
for (Class<?> aClass : PARTICLES) {
|
|
||||||
if (aClass.getSimpleName().equals(split[0])) {
|
|
||||||
clazz = aClass;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (clazz != null) {
|
|
||||||
try {
|
|
||||||
return (ParticleEnum) Enum.valueOf((Class<? extends Enum>) clazz, split[1]);
|
|
||||||
} catch (Exception e) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void openInventory(Player player, boolean onlyUnlocked) {
|
|
||||||
LobbyPlayer lobbyPlayer = LobbyPlayer.getLobbyPlayer(player.getUniqueId());
|
|
||||||
|
|
||||||
SteamwarUser user = SteamwarUser.get(player.getUniqueId());
|
|
||||||
Set<Integer> events = user.getTeam() == 0 ? new HashSet<>() : TeamTeilnahme.getEvents(user.getTeam()).stream().map(Event::getEventID).collect(Collectors.toSet());
|
|
||||||
String eggHuntConfig = UserConfig.getConfig(user.getId(), EggHunt.EGG_HUNT_CONFIG_KEY);
|
|
||||||
|
|
||||||
AtomicBoolean hasOneLocked = new AtomicBoolean(false);
|
|
||||||
List<SWListInv.SWListEntry<ParticleEnum>> particleList = new ArrayList<>();
|
|
||||||
for (Class<?> clazz : PARTICLES) {
|
|
||||||
addParticles(particleList, (ParticleEnum[]) clazz.getEnumConstants(), player, onlyUnlocked, hasOneLocked, user, events, eggHuntConfig);
|
|
||||||
}
|
|
||||||
for (int i = 0; i < particleList.size() % 45; i++) {
|
|
||||||
particleList.add(new SWListInv.SWListEntry<>(new SWItem(Material.BLACK_STAINED_GLASS_PANE, " "), null));
|
|
||||||
}
|
|
||||||
|
|
||||||
SWListInv<ParticleEnum> particleSWListInv = new SWListInv<>(player, LobbySystem.getMessage().parse("PARTICLE_INVENTORY", player), false, particleList, (clickType, particle) -> {
|
|
||||||
if (particle == null) return;
|
|
||||||
lobbyPlayer.setParticle(particle);
|
|
||||||
player.closeInventory();
|
|
||||||
if (ParticleListener.disabled(player)) {
|
|
||||||
LobbySystem.getMessage().send("PARTICLE_DEACTIVATED", player);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
particleSWListInv.setItem(48, Material.BARRIER, LobbySystem.getMessage().parse("PARTICLE_DESELECT", player), new ArrayList<>(), false, clickType -> {
|
|
||||||
lobbyPlayer.setParticle(null);
|
|
||||||
});
|
|
||||||
if (!hasOneLocked.get()) {
|
|
||||||
particleSWListInv.setItem(50, Material.GRAY_BANNER, LobbySystem.getMessage().parse("PARTICLE_SHOW_HAS_ALL", player), new ArrayList<>(), false, clickType -> {
|
|
||||||
});
|
|
||||||
} else if (onlyUnlocked) {
|
|
||||||
particleSWListInv.setItem(50, Material.LIME_BANNER, LobbySystem.getMessage().parse("PARTICLE_SHOW_ALL", player), new ArrayList<>(), false, clickType -> {
|
|
||||||
openInventory(player, false);
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
particleSWListInv.setItem(50, Material.RED_BANNER, LobbySystem.getMessage().parse("PARTICLE_SHOW_UNLOCKED", player), new ArrayList<>(), false, clickType -> {
|
|
||||||
openInventory(player, true);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
particleSWListInv.open();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void addParticles(List<SWListInv.SWListEntry<ParticleEnum>> particleList, ParticleEnum[] particleEnums, Player player, boolean onlyUnlocked, AtomicBoolean hasOneLocked, SteamwarUser user, Set<Integer> events, String eggHuntConfig) {
|
|
||||||
for (ParticleEnum particle : particleEnums) {
|
|
||||||
ParticleData particleData = particle.getParticle();
|
|
||||||
SWItem swItem = particleData.toSWItem(player, user, events, eggHuntConfig);
|
|
||||||
if (particleData.getRequirement().test(user, events, eggHuntConfig)) {
|
|
||||||
particleList.add(new SWListInv.SWListEntry<>(swItem, particle));
|
|
||||||
} else {
|
|
||||||
hasOneLocked.set(true);
|
|
||||||
if (!onlyUnlocked) {
|
|
||||||
particleList.add(new SWListInv.SWListEntry<>(swItem, null));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (int i = 0; i < particleList.size() % 9; i++) {
|
|
||||||
particleList.add(new SWListInv.SWListEntry<>(new SWItem(Material.BLACK_STAINED_GLASS_PANE, " "), null));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -23,20 +23,25 @@ import de.steamwar.linkage.Linked;
|
|||||||
import de.steamwar.lobby.LobbySystem;
|
import de.steamwar.lobby.LobbySystem;
|
||||||
import de.steamwar.lobby.jumpandrun.JumpAndRun;
|
import de.steamwar.lobby.jumpandrun.JumpAndRun;
|
||||||
import de.steamwar.lobby.listener.PlayerSpawn;
|
import de.steamwar.lobby.listener.PlayerSpawn;
|
||||||
|
import de.steamwar.lobby.particle.gui.ParticleMenu;
|
||||||
import de.steamwar.lobby.util.LobbyPlayer;
|
import de.steamwar.lobby.util.LobbyPlayer;
|
||||||
import de.steamwar.sql.SteamwarUser;
|
import de.steamwar.sql.SteamwarUser;
|
||||||
import de.steamwar.sql.UserPerm;
|
import de.steamwar.sql.UserPerm;
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.Location;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
import org.bukkit.event.EventHandler;
|
import org.bukkit.event.EventHandler;
|
||||||
import org.bukkit.event.EventPriority;
|
import org.bukkit.event.EventPriority;
|
||||||
import org.bukkit.event.Listener;
|
import org.bukkit.event.Listener;
|
||||||
import org.bukkit.event.player.PlayerInteractEntityEvent;
|
import org.bukkit.event.player.PlayerInteractEntityEvent;
|
||||||
import org.bukkit.event.player.PlayerInteractEvent;
|
import org.bukkit.event.player.PlayerInteractEvent;
|
||||||
|
import org.bukkit.event.player.PlayerJoinEvent;
|
||||||
import org.bukkit.event.player.PlayerMoveEvent;
|
import org.bukkit.event.player.PlayerMoveEvent;
|
||||||
import org.bukkit.event.player.PlayerQuitEvent;
|
import org.bukkit.event.player.PlayerQuitEvent;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
@Linked
|
@Linked
|
||||||
@@ -55,17 +60,14 @@ public class ParticleListener implements Listener {
|
|||||||
movingPlayers.entrySet().removeIf(entry -> entry.getValue() <= 0);
|
movingPlayers.entrySet().removeIf(entry -> entry.getValue() <= 0);
|
||||||
deg += 0.1;
|
deg += 0.1;
|
||||||
if (deg > 360) deg = 0;
|
if (deg > 360) deg = 0;
|
||||||
|
BodyMotion.nextTick();
|
||||||
|
Bukkit.getOnlinePlayers().forEach(BodyMotion::tick);
|
||||||
Bukkit.getOnlinePlayers().forEach(player -> {
|
Bukkit.getOnlinePlayers().forEach(player -> {
|
||||||
if (disabled(player)) return;
|
if (disabled(player)) return;
|
||||||
if (JumpAndRun.isPlayerInJumpAndRun(player)) return;
|
if (JumpAndRun.isPlayerInJumpAndRun(player)) return;
|
||||||
LobbyPlayer lobbyPlayer = LobbyPlayer.getLobbyPlayer(player.getUniqueId());
|
CompiledSet compiledSet = LobbyPlayer.getLobbyPlayer(player.getUniqueId()).getCompiledSet();
|
||||||
ParticleEnum particle = lobbyPlayer.getParticle();
|
if (compiledSet == null || !compiledSet.hasAlways()) return;
|
||||||
if (particle == null) return;
|
compiledSet.tickAlways(new ParticleTickData(player.getWorld(), player, deg, movingPlayers.containsKey(player), viewers(player)));
|
||||||
ParticleData particleData = particle.getParticle();
|
|
||||||
ParticleElement particleElement = particleData.getParticleElement();
|
|
||||||
if (particleElement.tickType() == ParticleTickType.ALWAYS) {
|
|
||||||
particleElement.tick(new ParticleTickData(player.getWorld(), player, deg, movingPlayers.containsKey(player)));
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}, 0, 1);
|
}, 0, 1);
|
||||||
}
|
}
|
||||||
@@ -77,7 +79,7 @@ public class ParticleListener implements Listener {
|
|||||||
if (!PlayerSpawn.PARTICLE.equals(event.getItem())) return;
|
if (!PlayerSpawn.PARTICLE.equals(event.getItem())) return;
|
||||||
event.setCancelled(true);
|
event.setCancelled(true);
|
||||||
|
|
||||||
ParticleInventory.openInventory(player, true);
|
ParticleMenu.open(player);
|
||||||
}
|
}
|
||||||
|
|
||||||
@EventHandler
|
@EventHandler
|
||||||
@@ -85,20 +87,23 @@ public class ParticleListener implements Listener {
|
|||||||
Player player = event.getPlayer();
|
Player player = event.getPlayer();
|
||||||
if (disabled(player)) return;
|
if (disabled(player)) return;
|
||||||
if (JumpAndRun.isPlayerInJumpAndRun(player)) return;
|
if (JumpAndRun.isPlayerInJumpAndRun(player)) return;
|
||||||
LobbyPlayer lobbyPlayer = LobbyPlayer.getLobbyPlayer(player.getUniqueId());
|
CompiledSet compiledSet = LobbyPlayer.getLobbyPlayer(player.getUniqueId()).getCompiledSet();
|
||||||
ParticleEnum particle = lobbyPlayer.getParticle();
|
if (compiledSet == null) return;
|
||||||
if (particle == null) return;
|
|
||||||
|
|
||||||
ParticleData particleData = particle.getParticle();
|
if (compiledSet.hasMove()) {
|
||||||
ParticleElement particleElement = particleData.getParticleElement();
|
compiledSet.tickMove(new ParticleTickData(player.getWorld(), player, deg, true, viewers(player)));
|
||||||
if (particleElement.tickType() == ParticleTickType.MOVE) {
|
|
||||||
particleElement.tick(new ParticleTickData(player.getWorld(), player, deg, true));
|
|
||||||
}
|
}
|
||||||
if (event.getFrom().getX() != event.getTo().getX() || event.getFrom().getY() != event.getTo().getY() || event.getFrom().getZ() != event.getTo().getZ()) {
|
if (event.getFrom().getX() != event.getTo().getX() || event.getFrom().getY() != event.getTo().getY() || event.getFrom().getZ() != event.getTo().getZ()) {
|
||||||
movingPlayers.put(player, 5);
|
movingPlayers.put(player, 5);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Ranks may have changed since the last session, which can lock or unlock the active slot
|
||||||
|
@EventHandler
|
||||||
|
public void onPlayerJoin(PlayerJoinEvent event) {
|
||||||
|
LobbyPlayer.getLobbyPlayer(event.getPlayer()).recompile();
|
||||||
|
}
|
||||||
|
|
||||||
@EventHandler(ignoreCancelled = true)
|
@EventHandler(ignoreCancelled = true)
|
||||||
public void onPlayerQuit(PlayerQuitEvent event) {
|
public void onPlayerQuit(PlayerQuitEvent event) {
|
||||||
movingPlayers.remove(event.getPlayer());
|
movingPlayers.remove(event.getPlayer());
|
||||||
@@ -111,6 +116,18 @@ public class ParticleListener implements Listener {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static List<Player> viewers(Player root) {
|
||||||
|
Location location = root.getLocation();
|
||||||
|
List<Player> viewers = new ArrayList<>();
|
||||||
|
for (Player player : root.getWorld().getPlayers()) {
|
||||||
|
int viewDistance = player.getClientViewDistance() * 16;
|
||||||
|
if (location.distanceSquared(player.getLocation()) <= viewDistance * viewDistance) {
|
||||||
|
viewers.add(player);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return viewers;
|
||||||
|
}
|
||||||
|
|
||||||
public static boolean disabled(Player player) {
|
public static boolean disabled(Player player) {
|
||||||
SteamwarUser user = SteamwarUser.get(player.getUniqueId());
|
SteamwarUser user = SteamwarUser.get(player.getUniqueId());
|
||||||
if (Bukkit.getOnlinePlayers().size() > SERVER_TEAM_MAX_SIZE) return true;
|
if (Bukkit.getOnlinePlayers().size() > SERVER_TEAM_MAX_SIZE) return true;
|
||||||
|
|||||||
@@ -39,7 +39,13 @@ public interface ParticleRequirement {
|
|||||||
|
|
||||||
String getRequirementName(Player player);
|
String getRequirementName(Player player);
|
||||||
|
|
||||||
boolean test(SteamwarUser user, Set<Integer> eventTeilname, String eggHuntConfig);
|
// Whether the player meets this requirement
|
||||||
|
boolean matches(UnlockContext context);
|
||||||
|
|
||||||
|
// Administrators pass every requirement; lore still names the regular requirement
|
||||||
|
default boolean test(UnlockContext context) {
|
||||||
|
return context.getUser().hasPerm(UserPerm.ADMINISTRATION) || matches(context);
|
||||||
|
}
|
||||||
|
|
||||||
ParticleRequirement SERVER_TEAM = new ParticleRequirement() {
|
ParticleRequirement SERVER_TEAM = new ParticleRequirement() {
|
||||||
@Override
|
@Override
|
||||||
@@ -48,8 +54,8 @@ public interface ParticleRequirement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean test(SteamwarUser user, Set<Integer> eventTeilname, String eggHuntConfig) {
|
public boolean matches(UnlockContext context) {
|
||||||
return user.hasPerm(UserPerm.TEAM);
|
return context.getUser().hasPerm(UserPerm.TEAM);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -60,21 +66,23 @@ public interface ParticleRequirement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean test(SteamwarUser user, Set<Integer> eventTeilname, String eggHuntConfig) {
|
public boolean matches(UnlockContext context) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
ParticleRequirement HAS_TEAM = new ParticleRequirement() {
|
|
||||||
|
// Does not also pass for the server team
|
||||||
|
ParticleRequirement TEAM_MEMBER = new ParticleRequirement() {
|
||||||
@Override
|
@Override
|
||||||
public String getRequirementName(Player player) {
|
public String getRequirementName(Player player) {
|
||||||
return LobbySystem.getMessage().parse("PARTICLE_UNLOCKED_BY_TEAM", player);
|
return LobbySystem.getMessage().parse("PARTICLE_UNLOCKED_BY_TEAM", player);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean test(SteamwarUser user, Set<Integer> eventTeilname, String eggHuntConfig) {
|
public boolean matches(UnlockContext context) {
|
||||||
return user.getTeam() != 0;
|
return context.getUser().getTeam() != 0;
|
||||||
}
|
}
|
||||||
}.or(SERVER_TEAM);
|
};
|
||||||
ParticleRequirement EVENT_PARTICIPATION = new ParticleRequirement() {
|
ParticleRequirement EVENT_PARTICIPATION = new ParticleRequirement() {
|
||||||
@Override
|
@Override
|
||||||
public String getRequirementName(Player player) {
|
public String getRequirementName(Player player) {
|
||||||
@@ -82,8 +90,8 @@ public interface ParticleRequirement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean test(SteamwarUser user, Set<Integer> eventTeilname, String eggHuntConfig) {
|
public boolean matches(UnlockContext context) {
|
||||||
return !eventTeilname.isEmpty();
|
return !context.getEvents().isEmpty();
|
||||||
}
|
}
|
||||||
}.or(SERVER_TEAM);
|
}.or(SERVER_TEAM);
|
||||||
ParticleRequirement EGG_HUNT_EASY = _easterEggHuntDifficulty(EggDifficulty.EASY);
|
ParticleRequirement EGG_HUNT_EASY = _easterEggHuntDifficulty(EggDifficulty.EASY);
|
||||||
@@ -98,7 +106,8 @@ public interface ParticleRequirement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean test(SteamwarUser user, Set<Integer> eventTeilname, String eggHuntConfig) {
|
public boolean matches(UnlockContext context) {
|
||||||
|
String eggHuntConfig = context.getEggHuntConfig();
|
||||||
if (eggHuntConfig == null) return false;
|
if (eggHuntConfig == null) return false;
|
||||||
int count = 0;
|
int count = 0;
|
||||||
for (int i = 0; i < eggHuntConfig.length(); i++) {
|
for (int i = 0; i < eggHuntConfig.length(); i++) {
|
||||||
@@ -120,8 +129,8 @@ public interface ParticleRequirement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean test(SteamwarUser user, Set<Integer> eventTeilname, String eggHuntConfig) {
|
public boolean matches(UnlockContext context) {
|
||||||
return user.getTeam() == teamId;
|
return context.getUser().getTeam() == teamId;
|
||||||
}
|
}
|
||||||
}.or(SERVER_TEAM);
|
}.or(SERVER_TEAM);
|
||||||
}
|
}
|
||||||
@@ -135,8 +144,8 @@ public interface ParticleRequirement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean test(SteamwarUser user, Set<Integer> eventTeilname, String eggHuntConfig) {
|
public boolean matches(UnlockContext context) {
|
||||||
return eventTeilname.contains(eventId);
|
return context.getEvents().contains(eventId);
|
||||||
}
|
}
|
||||||
}.or(SERVER_TEAM);
|
}.or(SERVER_TEAM);
|
||||||
}
|
}
|
||||||
@@ -152,9 +161,9 @@ public interface ParticleRequirement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean test(SteamwarUser user, Set<Integer> eventTeilname, String eggHuntConfig) {
|
public boolean matches(UnlockContext context) {
|
||||||
if (!eventTeilname.contains(eventId)) return false;
|
if (!teams.contains(context.getUser().getTeam())) return false;
|
||||||
return teams.contains(user.getTeam());
|
return context.getEvents().contains(eventId);
|
||||||
}
|
}
|
||||||
}.or(SERVER_TEAM);
|
}.or(SERVER_TEAM);
|
||||||
}
|
}
|
||||||
@@ -181,7 +190,8 @@ public interface ParticleRequirement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean test(SteamwarUser user, Set<Integer> eventTeilname, String eggHuntConfig) {
|
public boolean matches(UnlockContext context) {
|
||||||
|
String eggHuntConfig = context.getEggHuntConfig();
|
||||||
if (eggHuntConfig == null) return false;
|
if (eggHuntConfig == null) return false;
|
||||||
return eggs.stream().allMatch(id -> eggHuntConfig.length() > id && eggHuntConfig.charAt(id) == '1');
|
return eggs.stream().allMatch(id -> eggHuntConfig.length() > id && eggHuntConfig.charAt(id) == '1');
|
||||||
}
|
}
|
||||||
@@ -197,8 +207,8 @@ public interface ParticleRequirement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean test(SteamwarUser user, Set<Integer> eventTeilname, String eggHuntConfig) {
|
public boolean matches(UnlockContext context) {
|
||||||
return user.getId() == userId;
|
return context.getUser().getId() == userId;
|
||||||
}
|
}
|
||||||
}.or(SERVER_TEAM);
|
}.or(SERVER_TEAM);
|
||||||
}
|
}
|
||||||
@@ -212,8 +222,8 @@ public interface ParticleRequirement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean test(SteamwarUser user, Set<Integer> eventTeilname, String eggHuntConfig) {
|
public boolean matches(UnlockContext context) {
|
||||||
return user.getId() == userId;
|
return context.getUser().getId() == userId;
|
||||||
}
|
}
|
||||||
}.or(SERVER_TEAM);
|
}.or(SERVER_TEAM);
|
||||||
}
|
}
|
||||||
@@ -228,12 +238,31 @@ public interface ParticleRequirement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean test(SteamwarUser user, Set<Integer> eventTeilname, String eggHuntConfig) {
|
public boolean matches(UnlockContext context) {
|
||||||
return user.getTeam() == teamId;
|
return context.getUser().getTeam() == teamId;
|
||||||
}
|
}
|
||||||
}.or(SERVER_TEAM);
|
}.or(SERVER_TEAM);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Met by meeting any of the requirements; the lore lists them one per line
|
||||||
|
static ParticleRequirement anyOf(ParticleRequirement... requirements) {
|
||||||
|
return new ParticleRequirement() {
|
||||||
|
@Override
|
||||||
|
public String getRequirementName(Player player) {
|
||||||
|
return java.util.Arrays.stream(requirements).map(requirement -> requirement.getRequirementName(player)).filter(Objects::nonNull).collect(Collectors.joining("\n"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean matches(UnlockContext context) {
|
||||||
|
for (ParticleRequirement requirement : requirements) {
|
||||||
|
if (requirement.test(context)) return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// The alternative is checked first, as it is usually the cheap server team check
|
||||||
default ParticleRequirement or(ParticleRequirement particleRequirement) {
|
default ParticleRequirement or(ParticleRequirement particleRequirement) {
|
||||||
return new ParticleRequirement() {
|
return new ParticleRequirement() {
|
||||||
@Override
|
@Override
|
||||||
@@ -242,8 +271,8 @@ public interface ParticleRequirement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean test(SteamwarUser user, Set<Integer> eventTeilname, String eggHuntConfig) {
|
public boolean matches(UnlockContext context) {
|
||||||
return ParticleRequirement.this.test(user, eventTeilname, eggHuntConfig) || particleRequirement.test(user, eventTeilname, eggHuntConfig);
|
return particleRequirement.test(context) || ParticleRequirement.this.test(context);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,6 +26,8 @@ import org.bukkit.Location;
|
|||||||
import org.bukkit.World;
|
import org.bukkit.World;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
@AllArgsConstructor
|
@AllArgsConstructor
|
||||||
@Getter
|
@Getter
|
||||||
@@ -36,8 +38,13 @@ public class ParticleTickData {
|
|||||||
private Location location;
|
private Location location;
|
||||||
private final double deg;
|
private final double deg;
|
||||||
private final boolean isMoving;
|
private final boolean isMoving;
|
||||||
|
private final List<Player> viewers;
|
||||||
private boolean onlySelf = false;
|
private boolean onlySelf = false;
|
||||||
private boolean onlyOthers = false;
|
private boolean onlyOthers = false;
|
||||||
|
// Set for entries placed relative to the body, null for world placement
|
||||||
|
private BodyFrame frame;
|
||||||
|
// Where a point sits along its shape, from 0 to 1; when set, gradients colour by it instead of by time
|
||||||
|
private Double shade;
|
||||||
|
|
||||||
public ParticleTickData withLocation(Location location) {
|
public ParticleTickData withLocation(Location location) {
|
||||||
ParticleTickData particleTickData = copy();
|
ParticleTickData particleTickData = copy();
|
||||||
@@ -45,6 +52,24 @@ public class ParticleTickData {
|
|||||||
return particleTickData;
|
return particleTickData;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ParticleTickData withFrame(BodyFrame frame) {
|
||||||
|
ParticleTickData particleTickData = copy();
|
||||||
|
particleTickData.frame = frame;
|
||||||
|
particleTickData.location = frame.getOrigin().clone();
|
||||||
|
return particleTickData;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ParticleTickData withShade(double shade) {
|
||||||
|
ParticleTickData particleTickData = copy();
|
||||||
|
particleTickData.shade = shade;
|
||||||
|
return particleTickData;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The gradient position for this tick: along the shape when a shade is set, otherwise moving with time
|
||||||
|
public double gradientPosition() {
|
||||||
|
return shade != null ? Math.min(shade, 0.9999) * 360 : (deg * 10) % 360;
|
||||||
|
}
|
||||||
|
|
||||||
public ParticleTickData onlySelf() {
|
public ParticleTickData onlySelf() {
|
||||||
ParticleTickData particleTickData = copy();
|
ParticleTickData particleTickData = copy();
|
||||||
particleTickData.onlySelf = true;
|
particleTickData.onlySelf = true;
|
||||||
@@ -65,6 +90,6 @@ public class ParticleTickData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public ParticleTickData copy() {
|
public ParticleTickData copy() {
|
||||||
return new ParticleTickData(world, player, location, deg, isMoving, onlySelf, onlyOthers);
|
return new ParticleTickData(world, player, location, deg, isMoving, viewers, onlySelf, onlyOthers, frame, shade);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,59 @@
|
|||||||
|
/*
|
||||||
|
* This file is a part of the SteamWar software.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2026 SteamWar.de-Serverteam
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program 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 Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.steamwar.lobby.particle;
|
||||||
|
|
||||||
|
import de.steamwar.lobby.special.easter.EggHunt;
|
||||||
|
import de.steamwar.sql.Event;
|
||||||
|
import de.steamwar.sql.SteamwarUser;
|
||||||
|
import de.steamwar.sql.TeamTeilnahme;
|
||||||
|
import de.steamwar.sql.UserConfig;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
// Everything requirements check against, loaded only when a requirement needs it
|
||||||
|
public class UnlockContext {
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
private final SteamwarUser user;
|
||||||
|
private Set<Integer> events;
|
||||||
|
private String eggHuntConfig;
|
||||||
|
private boolean eggHuntConfigLoaded;
|
||||||
|
|
||||||
|
public UnlockContext(SteamwarUser user) {
|
||||||
|
this.user = user;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<Integer> getEvents() {
|
||||||
|
if (events == null) {
|
||||||
|
events = user.getTeam() == 0 ? Set.of() : TeamTeilnahme.getEvents(user.getTeam()).stream().map(Event::getEventID).collect(Collectors.toSet());
|
||||||
|
}
|
||||||
|
return events;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getEggHuntConfig() {
|
||||||
|
if (!eggHuntConfigLoaded) {
|
||||||
|
eggHuntConfig = UserConfig.getConfig(user.getId(), EggHunt.EGG_HUNT_CONFIG_KEY);
|
||||||
|
eggHuntConfigLoaded = true;
|
||||||
|
}
|
||||||
|
return eggHuntConfig;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
/*
|
||||||
|
* This file is a part of the SteamWar software.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2026 SteamWar.de-Serverteam
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program 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 Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.steamwar.lobby.particle.design;
|
||||||
|
|
||||||
|
import com.google.gson.JsonElement;
|
||||||
|
import com.google.gson.JsonPrimitive;
|
||||||
|
|
||||||
|
public class BooleanParameter extends Parameter<Boolean> {
|
||||||
|
|
||||||
|
public BooleanParameter(String key, String nameKey, boolean defaultValue) {
|
||||||
|
super(key, nameKey, defaultValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JsonElement toJson(Boolean value) {
|
||||||
|
return new JsonPrimitive(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Boolean read(JsonElement json) {
|
||||||
|
return json.getAsBoolean();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,102 @@
|
|||||||
|
/*
|
||||||
|
* This file is a part of the SteamWar software.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2026 SteamWar.de-Serverteam
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program 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 Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.steamwar.lobby.particle.design;
|
||||||
|
|
||||||
|
import com.google.gson.JsonElement;
|
||||||
|
import com.google.gson.JsonPrimitive;
|
||||||
|
import de.steamwar.lobby.particle.ParticleRequirement;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.function.Function;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
public class ChoiceParameter<E> extends Parameter<E> {
|
||||||
|
|
||||||
|
// Internal choices are usable by presets but never offered in the builder or accepted from storage. Locked choices
|
||||||
|
// are offered with their requirement; the requirement is looked up lazily as it may live in a reward design.
|
||||||
|
public record Choice<E>(String id, E value, boolean internal, Supplier<ParticleRequirement> requirement) {
|
||||||
|
|
||||||
|
public Choice(String id, E value, boolean internal) {
|
||||||
|
this(id, value, internal, () -> ParticleRequirement.NO_REQUIRMENT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private final List<Choice<E>> choices;
|
||||||
|
|
||||||
|
public ChoiceParameter(String key, String nameKey, E defaultValue, List<Choice<E>> choices) {
|
||||||
|
super(key, nameKey, defaultValue);
|
||||||
|
this.choices = choices;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <E extends Enum<E>> ChoiceParameter<E> ofEnum(String key, String nameKey, E defaultValue, Predicate<E> internal, Function<E, ParticleRequirement> requirement) {
|
||||||
|
List<Choice<E>> choices = new ArrayList<>();
|
||||||
|
for (E value : defaultValue.getDeclaringClass().getEnumConstants()) {
|
||||||
|
choices.add(new Choice<>(value.name(), value, internal.test(value), () -> requirement.apply(value)));
|
||||||
|
}
|
||||||
|
return new ChoiceParameter<>(key, nameKey, defaultValue, choices);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <E extends Enum<E>> ChoiceParameter<E> ofEnum(String key, String nameKey, E defaultValue, Predicate<E> internal) {
|
||||||
|
return ofEnum(key, nameKey, defaultValue, internal, value -> ParticleRequirement.NO_REQUIRMENT);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <E extends Enum<E>> ChoiceParameter<E> ofEnum(String key, String nameKey, E defaultValue) {
|
||||||
|
return ofEnum(key, nameKey, defaultValue, value -> false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<E> getBuildableValues() {
|
||||||
|
return choices.stream().filter(choice -> !choice.internal()).map(Choice::value).toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ParticleRequirement requirement(E value) {
|
||||||
|
return choiceOf(value).requirement().get();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JsonElement toJson(E value) {
|
||||||
|
return new JsonPrimitive(choiceOf(value).id());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected E read(JsonElement json) {
|
||||||
|
String id = json.getAsString();
|
||||||
|
return choices.stream()
|
||||||
|
.filter(choice -> !choice.internal() && choice.id().equals(id))
|
||||||
|
.map(Choice::value)
|
||||||
|
.findFirst()
|
||||||
|
.orElse(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public E validate(E value) {
|
||||||
|
return choices.stream().anyMatch(choice -> choice.value() == value) ? value : getDefaultValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Choice<E> choiceOf(E value) {
|
||||||
|
return choices.stream()
|
||||||
|
.filter(choice -> choice.value() == value)
|
||||||
|
.findFirst()
|
||||||
|
.orElseThrow(() -> new IllegalArgumentException("Unknown choice for " + getKey()));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
/*
|
||||||
|
* This file is a part of the SteamWar software.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2026 SteamWar.de-Serverteam
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program 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 Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.steamwar.lobby.particle.design;
|
||||||
|
|
||||||
|
public enum DesignVisibility {
|
||||||
|
// Offered in the builder to everyone
|
||||||
|
BASIC,
|
||||||
|
// Offered in the builder, usable once its requirement is met
|
||||||
|
REWARD,
|
||||||
|
// Only used by presets, never offered in the builder or accepted from storage
|
||||||
|
INTERNAL,
|
||||||
|
}
|
||||||
@@ -0,0 +1,201 @@
|
|||||||
|
/*
|
||||||
|
* This file is a part of the SteamWar software.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2026 SteamWar.de-Serverteam
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program 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 Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.steamwar.lobby.particle.design;
|
||||||
|
|
||||||
|
import de.steamwar.lobby.particle.ParticleElement;
|
||||||
|
import de.steamwar.lobby.particle.ParticleRequirement;
|
||||||
|
import de.steamwar.lobby.particle.DrawnWings;
|
||||||
|
import de.steamwar.lobby.particle.WingDesign;
|
||||||
|
import de.steamwar.lobby.particle.elements.Circle;
|
||||||
|
import de.steamwar.lobby.particle.elements.Cloud;
|
||||||
|
import de.steamwar.lobby.particle.elements.Wing;
|
||||||
|
import de.steamwar.lobby.particle.elements.custom.YOffset;
|
||||||
|
import lombok.experimental.UtilityClass;
|
||||||
|
import org.bukkit.Material;
|
||||||
|
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
@UtilityClass
|
||||||
|
public class Designs {
|
||||||
|
|
||||||
|
// Built on first use, so the design classes never depend on each other's initialisation order
|
||||||
|
private Map<String, ParticleDesign> registry;
|
||||||
|
|
||||||
|
public final ParticleDesign SIMPLE = new ParticleDesign("simple", Material.BLAZE_POWDER, "PARTICLE_DESIGN_SIMPLE") {
|
||||||
|
@Override
|
||||||
|
public ParticleElement build(ParameterValues values, ParticleElement particle) {
|
||||||
|
return particle;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
public final DoubleParameter CIRCLE_RADIUS = new DoubleParameter("radius", "PARTICLE_PARAM_RADIUS", 1, 0.2, 3, 0.1);
|
||||||
|
public final DoubleParameter CIRCLE_SPEED = new DoubleParameter("circleSpeed", "PARTICLE_PARAM_CIRCLE_SPEED", 1, -20, 20, 0.5);
|
||||||
|
public final DoubleParameter CIRCLE_PHASE = new DoubleParameter("phase", "PARTICLE_PARAM_PHASE", 0, 0, 345, 15);
|
||||||
|
public final DoubleParameter CIRCLE_RISE = new DoubleParameter("rise", "PARTICLE_PARAM_RISE", 0, 0, 3, 0.1);
|
||||||
|
public final DoubleParameter CIRCLE_RISE_SPEED = new DoubleParameter("riseSpeed", "PARTICLE_PARAM_RISE_SPEED", 20, 0, 100, 5);
|
||||||
|
public final BooleanParameter CIRCLE_BOUNCE = new BooleanParameter("bounce", "PARTICLE_PARAM_BOUNCE", true);
|
||||||
|
public final BooleanParameter CIRCLE_INVERTED = new BooleanParameter("inverted", "PARTICLE_PARAM_INVERTED", false);
|
||||||
|
|
||||||
|
public final ParticleDesign CIRCLE = new ParticleDesign("circle", Material.MAGMA_CREAM, "PARTICLE_DESIGN_CIRCLE",
|
||||||
|
CIRCLE_RADIUS, CIRCLE_SPEED, CIRCLE_PHASE, CIRCLE_RISE, CIRCLE_RISE_SPEED, CIRCLE_BOUNCE, CIRCLE_INVERTED) {
|
||||||
|
@Override
|
||||||
|
public ParticleElement build(ParameterValues values, ParticleElement particle) {
|
||||||
|
double rise = values.get(CIRCLE_RISE);
|
||||||
|
if (rise > 0) {
|
||||||
|
particle = new YOffset(particle, values.get(CIRCLE_RISE_SPEED), 0, rise, values.get(CIRCLE_INVERTED), values.get(CIRCLE_BOUNCE));
|
||||||
|
}
|
||||||
|
return new Circle(particle, values.get(CIRCLE_RADIUS), values.get(CIRCLE_SPEED), values.get(CIRCLE_PHASE));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Images keep their old ids, so saved wings still load; preset and tournament images stay out of the builder
|
||||||
|
private final List<ChoiceParameter.Choice<WingShape>> WING_SHAPES = List.of(
|
||||||
|
new ChoiceParameter.Choice<>("simple", WingShape.image(WingDesign.SIMPLE, Material.FEATHER), false),
|
||||||
|
new ChoiceParameter.Choice<>("complex", WingShape.image(WingDesign.COMPLEX, Material.PHANTOM_MEMBRANE), false),
|
||||||
|
new ChoiceParameter.Choice<>("sword", WingShape.image(WingDesign.SWORD, Material.STONE_SWORD), false),
|
||||||
|
new ChoiceParameter.Choice<>("bat", WingShape.drawn(DrawnWings.BAT, Material.BAT_SPAWN_EGG), false),
|
||||||
|
new ChoiceParameter.Choice<>("butterfly", WingShape.drawn(DrawnWings.BUTTERFLY, Material.PINK_PETALS), false),
|
||||||
|
new ChoiceParameter.Choice<>("fairy", WingShape.drawn(DrawnWings.FAIRY, Material.ALLAY_SPAWN_EGG), false),
|
||||||
|
// Tournament wing images, unlocked by the placement that earns their reward
|
||||||
|
new ChoiceParameter.Choice<>("mwgl", WingShape.image(WingDesign.MWGL, Material.IRON_SWORD), false, Unlocks.MINI_WARGEAR_LIGA_PLACEMENT),
|
||||||
|
new ChoiceParameter.Choice<>("sw", WingShape.image(WingDesign.SW, Material.CYAN_CARPET), false, Unlocks.UNDERWATER_MWG_PLACEMENT),
|
||||||
|
new ChoiceParameter.Choice<>("wgs", WingShape.image(WingDesign.WGS, Material.DIAMOND_HELMET), false, Unlocks.WARGEAR_SEASON_2022_PLACEMENT),
|
||||||
|
new ChoiceParameter.Choice<>("sword_crossed", WingShape.image(WingDesign.SWORD_CROSSED, Material.GOLDEN_SWORD), false, Unlocks.WARGEAR_CLASH_PLACEMENT),
|
||||||
|
new ChoiceParameter.Choice<>("eclipse", WingShape.image(WingDesign.ECLIPSE), true),
|
||||||
|
new ChoiceParameter.Choice<>("pl", WingShape.image(WingDesign.PL), true),
|
||||||
|
new ChoiceParameter.Choice<>("plompa_easter", WingShape.image(WingDesign.PlompaEasterWings), true),
|
||||||
|
new ChoiceParameter.Choice<>("ely_e", WingShape.image(WingDesign.ELY_E), true),
|
||||||
|
new ChoiceParameter.Choice<>("ely_l", WingShape.image(WingDesign.ELY_L), true),
|
||||||
|
new ChoiceParameter.Choice<>("ely_y", WingShape.image(WingDesign.ELY_Y), true),
|
||||||
|
new ChoiceParameter.Choice<>("ev", WingShape.image(WingDesign.EV), true)
|
||||||
|
);
|
||||||
|
public final ChoiceParameter<WingShape> WING_SHAPE = new ChoiceParameter<>("image", "PARTICLE_PARAM_WING_IMAGE", WING_SHAPES.get(0).value(), WING_SHAPES);
|
||||||
|
// Points per wing of drawn shapes; it replaces the amount, as every point spawns one particle
|
||||||
|
public final IntParameter WING_DETAIL = new IntParameter("detail", "PARTICLE_PARAM_WING_DETAIL", 20, 6, 60);
|
||||||
|
public final DoubleParameter WING_SIZE = new DoubleParameter("size", "PARTICLE_PARAM_WING_SIZE", 0.15, 0.05, 0.3, 0.01);
|
||||||
|
|
||||||
|
public enum WingColoring {
|
||||||
|
// The whole wing changes colour through the entry's colours over time
|
||||||
|
OVER_TIME,
|
||||||
|
// The colours run from the spine to the wing tips
|
||||||
|
ALONG_WING,
|
||||||
|
}
|
||||||
|
|
||||||
|
public final ChoiceParameter<WingColoring> WING_COLORING = ChoiceParameter.ofEnum("coloring", "PARTICLE_PARAM_WING_COLORING", WingColoring.OVER_TIME);
|
||||||
|
// Percent of the points shown each tick; fewer makes the wing twinkle and costs less
|
||||||
|
public final IntParameter WING_SPARKLE = new IntParameter("sparkle", "PARTICLE_PARAM_WING_SPARKLE", 100, 10, 100);
|
||||||
|
public final ChoiceParameter<Wing.Motion> WING_MOTION = ChoiceParameter.ofEnum("motion", "PARTICLE_PARAM_WING_MOTION", Wing.Motion.NONE, motion -> false,
|
||||||
|
motion -> motion == Wing.Motion.BEAT_ON_JUMP ? Unlocks.WARGEAR_SEASON_2022.get() : ParticleRequirement.NO_REQUIRMENT);
|
||||||
|
// Moving wings need to be redrawn often to look smooth; still ones are fine once a second
|
||||||
|
private final int MOVING_WING_INTERVAL = 2;
|
||||||
|
private final int STILL_WING_INTERVAL = 20;
|
||||||
|
|
||||||
|
public final ParticleDesign WING = new ParticleDesign("wing", Material.ELYTRA, "PARTICLE_DESIGN_WING", WING_SHAPE, WING_DETAIL, WING_SIZE, WING_COLORING, WING_SPARKLE, WING_MOTION) {
|
||||||
|
@Override
|
||||||
|
public ParticleElement build(ParameterValues values, ParticleElement particle) {
|
||||||
|
WingShape shape = values.get(WING_SHAPE);
|
||||||
|
return new Wing(particle, values.get(WING_SIZE), shape.design(values.get(WING_DETAIL)),
|
||||||
|
values.get(WING_COLORING) == WingColoring.ALONG_WING, values.get(WING_SPARKLE), values.get(WING_MOTION));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean redrawsAfterJump(ParameterValues values) {
|
||||||
|
return values.get(WING_MOTION) == Wing.Motion.BEAT_ON_JUMP;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int points(ParameterValues values) {
|
||||||
|
int points = values.get(WING_SHAPE).points(values.get(WING_DETAIL));
|
||||||
|
return (int) Math.ceil(points * values.get(WING_SPARKLE) / 100.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean showsSetting(Parameter<?> parameter, ParameterValues values) {
|
||||||
|
return parameter != WING_DETAIL || values.get(WING_SHAPE).isDrawn();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean replacesAmount(ParameterValues values) {
|
||||||
|
return values.get(WING_SHAPE).isDrawn();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void adjust(Parameter<?> changed, ParameterValues values) {
|
||||||
|
if (changed != WING_MOTION) return;
|
||||||
|
// A beat on jumping is redrawn every tick only while it beats
|
||||||
|
boolean moving = values.get(WING_MOTION) == Wing.Motion.FLAP_SLOW || values.get(WING_MOTION) == Wing.Motion.FLAP_FAST;
|
||||||
|
int interval = values.get(EntryParameters.INTERVAL);
|
||||||
|
if (moving && interval > MOVING_WING_INTERVAL) {
|
||||||
|
values.set(EntryParameters.INTERVAL, MOVING_WING_INTERVAL);
|
||||||
|
} else if (!moving && interval == MOVING_WING_INTERVAL) {
|
||||||
|
values.set(EntryParameters.INTERVAL, STILL_WING_INTERVAL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Image wings spawn a particle on every pixel, so they start sparse, and sit on the back even while gliding
|
||||||
|
@Override
|
||||||
|
public void applyDefaults(ParameterValues values) {
|
||||||
|
values.set(EntryParameters.COUNT, 1)
|
||||||
|
.set(EntryParameters.SPEED, 0.0)
|
||||||
|
.set(EntryParameters.TICK_MODE, EntryParameters.TickMode.ALWAYS)
|
||||||
|
.set(EntryParameters.INTERVAL, STILL_WING_INTERVAL)
|
||||||
|
.set(EntryParameters.PLACEMENT, EntryParameters.Placement.BODY);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// The wing shape that shows the given image, for presets
|
||||||
|
public WingShape wingShape(WingDesign image) {
|
||||||
|
return WING_SHAPE.getChoices().stream().map(ChoiceParameter.Choice::value).filter(shape -> shape.shows(image)).findFirst().orElseThrow();
|
||||||
|
}
|
||||||
|
|
||||||
|
public final ParticleDesign CLOUD = new ParticleDesign("cloud", Material.WHITE_WOOL, "PARTICLE_DESIGN_CLOUD") {
|
||||||
|
@Override
|
||||||
|
public ParticleElement build(ParameterValues values, ParticleElement particle) {
|
||||||
|
return new Cloud(particle);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private Map<String, ParticleDesign> registry() {
|
||||||
|
if (registry == null) {
|
||||||
|
Map<String, ParticleDesign> designs = new LinkedHashMap<>();
|
||||||
|
List.of(SIMPLE, CIRCLE, WING, CLOUD).forEach(design -> register(designs, design));
|
||||||
|
RewardDesigns.all().forEach(design -> register(designs, design));
|
||||||
|
InternalDesigns.all().forEach(design -> register(designs, design));
|
||||||
|
registry = designs;
|
||||||
|
}
|
||||||
|
return registry;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void register(Map<String, ParticleDesign> designs, ParticleDesign design) {
|
||||||
|
if (designs.put(design.getId(), design) != null) {
|
||||||
|
throw new IllegalStateException("Duplicate particle design " + design.getId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public ParticleDesign byId(String id) {
|
||||||
|
return registry().get(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<ParticleDesign> all() {
|
||||||
|
return List.copyOf(registry().values());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
/*
|
||||||
|
* This file is a part of the SteamWar software.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2026 SteamWar.de-Serverteam
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program 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 Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.steamwar.lobby.particle.design;
|
||||||
|
|
||||||
|
import com.google.gson.JsonElement;
|
||||||
|
import com.google.gson.JsonPrimitive;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
public class DoubleParameter extends Parameter<Double> {
|
||||||
|
|
||||||
|
private final double min;
|
||||||
|
private final double max;
|
||||||
|
private final double step;
|
||||||
|
|
||||||
|
public DoubleParameter(String key, String nameKey, double defaultValue, double min, double max, double step) {
|
||||||
|
super(key, nameKey, defaultValue);
|
||||||
|
this.min = min;
|
||||||
|
this.max = max;
|
||||||
|
this.step = step;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JsonElement toJson(Double value) {
|
||||||
|
return new JsonPrimitive(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Double read(JsonElement json) {
|
||||||
|
return json.getAsDouble();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Double validate(Double value) {
|
||||||
|
if (value.isNaN()) {
|
||||||
|
return getDefaultValue();
|
||||||
|
}
|
||||||
|
return Math.max(min, Math.min(max, value));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,227 @@
|
|||||||
|
/*
|
||||||
|
* This file is a part of the SteamWar software.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2026 SteamWar.de-Serverteam
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program 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 Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.steamwar.lobby.particle.design;
|
||||||
|
|
||||||
|
import de.steamwar.lobby.particle.ParticleRequirement;
|
||||||
|
import de.steamwar.lobby.particle.ParticleElement;
|
||||||
|
import de.steamwar.lobby.particle.elements.*;
|
||||||
|
import de.steamwar.lobby.particle.elements.custom.NonMoving;
|
||||||
|
import lombok.experimental.UtilityClass;
|
||||||
|
import org.bukkit.Material;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.function.UnaryOperator;
|
||||||
|
|
||||||
|
// Parameters every entry has, independent of its design
|
||||||
|
@UtilityClass
|
||||||
|
public class EntryParameters {
|
||||||
|
|
||||||
|
public enum TickMode {
|
||||||
|
MOVE,
|
||||||
|
ALWAYS,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Body places offsets, heights, rings and wings around the player's body, even while gliding or swimming
|
||||||
|
public enum Placement {
|
||||||
|
WORLD,
|
||||||
|
BODY,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enchanting glyphs can fly between each point and the player instead of floating down
|
||||||
|
public enum GlyphFlight {
|
||||||
|
NORMAL,
|
||||||
|
INTO_PLAYER,
|
||||||
|
OUT_OF_PLAYER,
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum Movement {
|
||||||
|
ANY(element -> element),
|
||||||
|
MOVING(element -> data -> {
|
||||||
|
if (data.isMoving()) element.tick(data);
|
||||||
|
}),
|
||||||
|
STANDING_STILL(NonMoving::new),
|
||||||
|
;
|
||||||
|
|
||||||
|
private final UnaryOperator<ParticleElement> wrapper;
|
||||||
|
|
||||||
|
Movement(UnaryOperator<ParticleElement> wrapper) {
|
||||||
|
this.wrapper = wrapper;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ParticleElement wrap(ParticleElement element) {
|
||||||
|
return wrapper.apply(element);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum Gliding {
|
||||||
|
ANY(element -> element),
|
||||||
|
GLIDING(Flying::new),
|
||||||
|
NOT_GLIDING(NonFlying::new),
|
||||||
|
;
|
||||||
|
|
||||||
|
private final UnaryOperator<ParticleElement> wrapper;
|
||||||
|
|
||||||
|
Gliding(UnaryOperator<ParticleElement> wrapper) {
|
||||||
|
this.wrapper = wrapper;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ParticleElement wrap(ParticleElement element) {
|
||||||
|
return wrapper.apply(element);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum Ground {
|
||||||
|
ANY(element -> element),
|
||||||
|
ON_GROUND(Floor::new),
|
||||||
|
IN_AIR(NonFloor::new),
|
||||||
|
;
|
||||||
|
|
||||||
|
private final UnaryOperator<ParticleElement> wrapper;
|
||||||
|
|
||||||
|
Ground(UnaryOperator<ParticleElement> wrapper) {
|
||||||
|
this.wrapper = wrapper;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ParticleElement wrap(ParticleElement element) {
|
||||||
|
return wrapper.apply(element);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum Sneak {
|
||||||
|
ANY(element -> element),
|
||||||
|
SNEAKING(Sneaking::new),
|
||||||
|
NOT_SNEAKING(element -> data -> {
|
||||||
|
if (!data.getPlayer().isSneaking()) element.tick(data);
|
||||||
|
}),
|
||||||
|
;
|
||||||
|
|
||||||
|
private final UnaryOperator<ParticleElement> wrapper;
|
||||||
|
|
||||||
|
Sneak(UnaryOperator<ParticleElement> wrapper) {
|
||||||
|
this.wrapper = wrapper;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ParticleElement wrap(ParticleElement element) {
|
||||||
|
return wrapper.apply(element);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum Visibility {
|
||||||
|
EVERYONE(element -> element),
|
||||||
|
ONLY_SELF(OnlySelf::new),
|
||||||
|
ONLY_OTHERS(OnlyOthers::new),
|
||||||
|
;
|
||||||
|
|
||||||
|
private final UnaryOperator<ParticleElement> wrapper;
|
||||||
|
|
||||||
|
Visibility(UnaryOperator<ParticleElement> wrapper) {
|
||||||
|
this.wrapper = wrapper;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ParticleElement wrap(ParticleElement element) {
|
||||||
|
return wrapper.apply(element);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public final ChoiceParameter<ParticleType> PARTICLE = ChoiceParameter.ofEnum("particle", "PARTICLE_PARAM_PARTICLE", ParticleType.FLAME, type -> !type.isBuildable(), ParticleType::getRequirement);
|
||||||
|
public final IntParameter COUNT = new IntParameter("count", "PARTICLE_PARAM_COUNT", 5, 1, 20);
|
||||||
|
// Minecraft scatters particles around their point by about this many blocks; beyond half a block a design dissolves
|
||||||
|
public final DoubleParameter SPREAD_X = new DoubleParameter("spreadX", "PARTICLE_PARAM_SPREAD_X", 0, 0, 0.5, 0.01);
|
||||||
|
public final DoubleParameter SPREAD_Y = new DoubleParameter("spreadY", "PARTICLE_PARAM_SPREAD_Y", 0, 0, 0.5, 0.01);
|
||||||
|
public final DoubleParameter SPREAD_Z = new DoubleParameter("spreadZ", "PARTICLE_PARAM_SPREAD_Z", 0, 0, 0.5, 0.01);
|
||||||
|
// For most particles the speed of their random spray; above 0.2 they shoot blocks away
|
||||||
|
public final DoubleParameter SPEED = new DoubleParameter("speed", "PARTICLE_PARAM_SPEED", 0.01, 0, 0.05, 0.005);
|
||||||
|
public final GradientParameter COLORS = new GradientParameter("colors", "PARTICLE_PARAM_COLORS", 8);
|
||||||
|
// Blocks that are also items, so they work for block and item particles alike
|
||||||
|
public final ChoiceParameter<Material> BLOCK = new ChoiceParameter<>("block", "PARTICLE_PARAM_BLOCK", Material.STONE, List.of(
|
||||||
|
new ChoiceParameter.Choice<>("stone", Material.STONE, false),
|
||||||
|
new ChoiceParameter.Choice<>("cobblestone", Material.COBBLESTONE, false),
|
||||||
|
new ChoiceParameter.Choice<>("dirt", Material.DIRT, false),
|
||||||
|
new ChoiceParameter.Choice<>("grass_block", Material.GRASS_BLOCK, false),
|
||||||
|
new ChoiceParameter.Choice<>("sand", Material.SAND, false),
|
||||||
|
new ChoiceParameter.Choice<>("red_sand", Material.RED_SAND, false),
|
||||||
|
new ChoiceParameter.Choice<>("gravel", Material.GRAVEL, false),
|
||||||
|
new ChoiceParameter.Choice<>("oak_planks", Material.OAK_PLANKS, false),
|
||||||
|
new ChoiceParameter.Choice<>("oak_leaves", Material.OAK_LEAVES, false),
|
||||||
|
new ChoiceParameter.Choice<>("cherry_leaves", Material.CHERRY_LEAVES, false),
|
||||||
|
new ChoiceParameter.Choice<>("snow_block", Material.SNOW_BLOCK, false),
|
||||||
|
new ChoiceParameter.Choice<>("ice", Material.ICE, false),
|
||||||
|
new ChoiceParameter.Choice<>("glass", Material.GLASS, false),
|
||||||
|
new ChoiceParameter.Choice<>("tnt", Material.TNT, false),
|
||||||
|
new ChoiceParameter.Choice<>("pumpkin", Material.PUMPKIN, false),
|
||||||
|
new ChoiceParameter.Choice<>("melon", Material.MELON, false),
|
||||||
|
new ChoiceParameter.Choice<>("honey_block", Material.HONEY_BLOCK, false),
|
||||||
|
new ChoiceParameter.Choice<>("slime_block", Material.SLIME_BLOCK, false),
|
||||||
|
new ChoiceParameter.Choice<>("netherrack", Material.NETHERRACK, false),
|
||||||
|
new ChoiceParameter.Choice<>("soul_sand", Material.SOUL_SAND, false),
|
||||||
|
new ChoiceParameter.Choice<>("magma_block", Material.MAGMA_BLOCK, false),
|
||||||
|
new ChoiceParameter.Choice<>("end_stone", Material.END_STONE, false),
|
||||||
|
new ChoiceParameter.Choice<>("obsidian", Material.OBSIDIAN, false),
|
||||||
|
new ChoiceParameter.Choice<>("prismarine", Material.PRISMARINE, false),
|
||||||
|
new ChoiceParameter.Choice<>("sculk", Material.SCULK, false),
|
||||||
|
new ChoiceParameter.Choice<>("amethyst_block", Material.AMETHYST_BLOCK, false),
|
||||||
|
new ChoiceParameter.Choice<>("coal_block", Material.COAL_BLOCK, false),
|
||||||
|
new ChoiceParameter.Choice<>("iron_block", Material.IRON_BLOCK, false),
|
||||||
|
new ChoiceParameter.Choice<>("gold_block", Material.GOLD_BLOCK, false),
|
||||||
|
new ChoiceParameter.Choice<>("redstone_block", Material.REDSTONE_BLOCK, false),
|
||||||
|
new ChoiceParameter.Choice<>("lapis_block", Material.LAPIS_BLOCK, false),
|
||||||
|
new ChoiceParameter.Choice<>("emerald_block", Material.EMERALD_BLOCK, false),
|
||||||
|
new ChoiceParameter.Choice<>("diamond_block", Material.DIAMOND_BLOCK, false),
|
||||||
|
new ChoiceParameter.Choice<>("white_wool", Material.WHITE_WOOL, false),
|
||||||
|
new ChoiceParameter.Choice<>("red_wool", Material.RED_WOOL, false),
|
||||||
|
new ChoiceParameter.Choice<>("orange_wool", Material.ORANGE_WOOL, false),
|
||||||
|
new ChoiceParameter.Choice<>("yellow_wool", Material.YELLOW_WOOL, false),
|
||||||
|
new ChoiceParameter.Choice<>("lime_wool", Material.LIME_WOOL, false),
|
||||||
|
new ChoiceParameter.Choice<>("light_blue_wool", Material.LIGHT_BLUE_WOOL, false),
|
||||||
|
new ChoiceParameter.Choice<>("blue_wool", Material.BLUE_WOOL, false),
|
||||||
|
new ChoiceParameter.Choice<>("purple_wool", Material.PURPLE_WOOL, false),
|
||||||
|
new ChoiceParameter.Choice<>("pink_wool", Material.PINK_WOOL, false),
|
||||||
|
new ChoiceParameter.Choice<>("black_wool", Material.BLACK_WOOL, false)
|
||||||
|
));
|
||||||
|
public final DoubleParameter OFFSET_X = new DoubleParameter("offsetX", "PARTICLE_PARAM_OFFSET_X", 0, -3, 3, 0.1);
|
||||||
|
public final DoubleParameter OFFSET_Y = new DoubleParameter("offsetY", "PARTICLE_PARAM_OFFSET_Y", 0, -3, 3, 0.1);
|
||||||
|
public final DoubleParameter OFFSET_Z = new DoubleParameter("offsetZ", "PARTICLE_PARAM_OFFSET_Z", 0, -3, 3, 0.1);
|
||||||
|
public final IntParameter INTERVAL = new IntParameter("interval", "PARTICLE_PARAM_INTERVAL", 1, 1, 100);
|
||||||
|
public final IntParameter TRAIL_DURATION = new IntParameter("trailDuration", "PARTICLE_PARAM_TRAIL_DURATION", 20, 1, 200);
|
||||||
|
// Where a trail particle flies to, relative to where it spawns
|
||||||
|
public final DoubleParameter TRAIL_TARGET_X = new DoubleParameter("trailTargetX", "PARTICLE_PARAM_TRAIL_TARGET_X", 0, -5, 5, 0.1);
|
||||||
|
public final DoubleParameter TRAIL_TARGET_Y = new DoubleParameter("trailTargetY", "PARTICLE_PARAM_TRAIL_TARGET_Y", 1, -5, 5, 0.1);
|
||||||
|
public final DoubleParameter TRAIL_TARGET_Z = new DoubleParameter("trailTargetZ", "PARTICLE_PARAM_TRAIL_TARGET_Z", 0, -5, 5, 0.1);
|
||||||
|
public final ChoiceParameter<GlyphFlight> GLYPH_FLIGHT = ChoiceParameter.ofEnum("glyphFlight", "PARTICLE_PARAM_GLYPH_FLIGHT", GlyphFlight.NORMAL, flight -> false,
|
||||||
|
flight -> switch (flight) {
|
||||||
|
case NORMAL -> ParticleRequirement.NO_REQUIRMENT;
|
||||||
|
case INTO_PLAYER -> Unlocks.WARGEAR_SEASON_PLACEMENT.get();
|
||||||
|
case OUT_OF_PLAYER -> Unlocks.WARGEAR_SEASON.get();
|
||||||
|
});
|
||||||
|
public final ChoiceParameter<Placement> PLACEMENT = ChoiceParameter.ofEnum("placement", "PARTICLE_PARAM_PLACEMENT", Placement.WORLD);
|
||||||
|
public final ChoiceParameter<TickMode> TICK_MODE = ChoiceParameter.ofEnum("tickMode", "PARTICLE_PARAM_TICK_MODE", TickMode.MOVE);
|
||||||
|
public final ChoiceParameter<Movement> MOVEMENT = ChoiceParameter.ofEnum("movement", "PARTICLE_PARAM_MOVEMENT", Movement.ANY);
|
||||||
|
public final ChoiceParameter<Gliding> GLIDING = ChoiceParameter.ofEnum("gliding", "PARTICLE_PARAM_GLIDING", Gliding.ANY);
|
||||||
|
public final ChoiceParameter<Ground> GROUND = ChoiceParameter.ofEnum("ground", "PARTICLE_PARAM_GROUND", Ground.ANY);
|
||||||
|
public final ChoiceParameter<Sneak> SNEAK = ChoiceParameter.ofEnum("sneak", "PARTICLE_PARAM_SNEAK", Sneak.ANY);
|
||||||
|
public final ChoiceParameter<Visibility> VISIBILITY = ChoiceParameter.ofEnum("visibility", "PARTICLE_PARAM_VISIBILITY", Visibility.EVERYONE);
|
||||||
|
|
||||||
|
public final List<Parameter<?>> ALL = List.of(
|
||||||
|
PARTICLE, GLYPH_FLIGHT, COUNT, SPREAD_X, SPREAD_Y, SPREAD_Z, SPEED, COLORS, BLOCK,
|
||||||
|
TRAIL_DURATION, TRAIL_TARGET_X, TRAIL_TARGET_Y, TRAIL_TARGET_Z,
|
||||||
|
OFFSET_X, OFFSET_Y, OFFSET_Z, PLACEMENT, INTERVAL, TICK_MODE,
|
||||||
|
MOVEMENT, GLIDING, GROUND, SNEAK, VISIBILITY
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
/*
|
||||||
|
* This file is a part of the SteamWar software.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2026 SteamWar.de-Serverteam
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program 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 Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.steamwar.lobby.particle.design;
|
||||||
|
|
||||||
|
import de.steamwar.lobby.particle.ParticleElement;
|
||||||
|
import de.steamwar.lobby.particle.ParticleRequirement;
|
||||||
|
import org.bukkit.Material;
|
||||||
|
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
// A design whose look, including its particles, is fixed; only the entry's conditions, timing and offset apply
|
||||||
|
public class FixedDesign extends ParticleDesign {
|
||||||
|
|
||||||
|
private final int points;
|
||||||
|
private final Consumer<ParameterValues> defaults;
|
||||||
|
private final Supplier<ParticleElement> shape;
|
||||||
|
|
||||||
|
public FixedDesign(String id, Material icon, String nameKey, ParticleRequirement requirement, int points, Consumer<ParameterValues> defaults, Supplier<ParticleElement> shape) {
|
||||||
|
super(id, icon, nameKey, DesignVisibility.REWARD, requirement);
|
||||||
|
this.points = points;
|
||||||
|
this.defaults = defaults;
|
||||||
|
this.shape = shape;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ParticleElement build(ParameterValues values, ParticleElement particle) {
|
||||||
|
return shape.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int points(ParameterValues values) {
|
||||||
|
return points;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean usesEntryParticle() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void applyDefaults(ParameterValues values) {
|
||||||
|
defaults.accept(values);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
/*
|
||||||
|
* This file is a part of the SteamWar software.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2026 SteamWar.de-Serverteam
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program 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 Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.steamwar.lobby.particle.design;
|
||||||
|
|
||||||
|
import com.google.gson.JsonArray;
|
||||||
|
import com.google.gson.JsonElement;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
import java.awt.Color;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
// An empty gradient means a random colour per spawn
|
||||||
|
@Getter
|
||||||
|
public class GradientParameter extends Parameter<List<Color>> {
|
||||||
|
|
||||||
|
private final int maxColors;
|
||||||
|
|
||||||
|
public GradientParameter(String key, String nameKey, int maxColors) {
|
||||||
|
super(key, nameKey, List.of());
|
||||||
|
this.maxColors = maxColors;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JsonElement toJson(List<Color> value) {
|
||||||
|
JsonArray array = new JsonArray();
|
||||||
|
value.forEach(color -> array.add(color.getRGB() & 0xFFFFFF));
|
||||||
|
return array;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected List<Color> read(JsonElement json) {
|
||||||
|
List<Color> colors = new ArrayList<>();
|
||||||
|
for (JsonElement element : json.getAsJsonArray()) {
|
||||||
|
colors.add(new Color(element.getAsInt() & 0xFFFFFF));
|
||||||
|
}
|
||||||
|
return colors;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Color> validate(List<Color> value) {
|
||||||
|
return List.copyOf(value.size() > maxColors ? value.subList(0, maxColors) : value);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
/*
|
||||||
|
* This file is a part of the SteamWar software.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2026 SteamWar.de-Serverteam
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program 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 Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.steamwar.lobby.particle.design;
|
||||||
|
|
||||||
|
import com.google.gson.JsonElement;
|
||||||
|
import com.google.gson.JsonPrimitive;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
public class IntParameter extends Parameter<Integer> {
|
||||||
|
|
||||||
|
private final int min;
|
||||||
|
private final int max;
|
||||||
|
|
||||||
|
public IntParameter(String key, String nameKey, int defaultValue, int min, int max) {
|
||||||
|
super(key, nameKey, defaultValue);
|
||||||
|
this.min = min;
|
||||||
|
this.max = max;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JsonElement toJson(Integer value) {
|
||||||
|
return new JsonPrimitive(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Integer read(JsonElement json) {
|
||||||
|
return json.getAsInt();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Integer validate(Integer value) {
|
||||||
|
return Math.max(min, Math.min(max, value));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,85 @@
|
|||||||
|
/*
|
||||||
|
* This file is a part of the SteamWar software.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2026 SteamWar.de-Serverteam
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program 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 Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.steamwar.lobby.particle.design;
|
||||||
|
|
||||||
|
import de.steamwar.lobby.particle.ParticleElement;
|
||||||
|
import de.steamwar.lobby.particle.ParticleRequirement;
|
||||||
|
import de.steamwar.lobby.particle.elements.Circle;
|
||||||
|
import de.steamwar.lobby.particle.elements.custom.HearthBeat;
|
||||||
|
import de.steamwar.lobby.particle.elements.custom.PulseShimmer;
|
||||||
|
import de.steamwar.lobby.particle.elements.custom.YOffset;
|
||||||
|
import lombok.experimental.UtilityClass;
|
||||||
|
import org.bukkit.Material;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
// Shapes only presets use; they are never offered in the builder
|
||||||
|
@UtilityClass
|
||||||
|
public class InternalDesigns {
|
||||||
|
|
||||||
|
public final IntParameter PULSE_SPEED = new IntParameter("pulseSpeed", "PARTICLE_PARAM_PULSE_SPEED", 80, 1, 200);
|
||||||
|
public final IntParameter PULSE_ROTATION = new IntParameter("pulseRotation", "PARTICLE_PARAM_PULSE_ROTATION", 2, 0, 20);
|
||||||
|
|
||||||
|
public final ParticleDesign PULSE_SHIMMER = new ParticleDesign("pulse_shimmer", Material.GRAY_CANDLE, "PARTICLE_DESIGN_PULSE_SHIMMER", DesignVisibility.INTERNAL, ParticleRequirement.NO_REQUIRMENT,
|
||||||
|
PULSE_SPEED, PULSE_ROTATION) {
|
||||||
|
@Override
|
||||||
|
public ParticleElement build(ParameterValues values, ParticleElement particle) {
|
||||||
|
return new PulseShimmer(particle, values.get(PULSE_SPEED), values.get(PULSE_ROTATION));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int points(ParameterValues values) {
|
||||||
|
return 3;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
public final DoubleParameter HEART_BEAT_SPEED = new DoubleParameter("heartBeatSpeed", "PARTICLE_PARAM_HEART_BEAT_SPEED", 80, 1, 200, 5);
|
||||||
|
|
||||||
|
public final ParticleDesign HEART_BEAT = new ParticleDesign("heart_beat", Material.APPLE, "PARTICLE_DESIGN_HEART_BEAT", DesignVisibility.INTERNAL, ParticleRequirement.NO_REQUIRMENT,
|
||||||
|
HEART_BEAT_SPEED) {
|
||||||
|
@Override
|
||||||
|
public ParticleElement build(ParameterValues values, ParticleElement particle) {
|
||||||
|
return new HearthBeat(particle, values.get(HEART_BEAT_SPEED));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
public final DoubleParameter ORBIT_RADIUS = new DoubleParameter("orbitRadius", "PARTICLE_PARAM_ORBIT_RADIUS", 1.5, 0.2, 3, 0.1);
|
||||||
|
public final DoubleParameter ORBIT_SPEED = new DoubleParameter("orbitSpeed", "PARTICLE_PARAM_ORBIT_SPEED", 1, -20, 20, 0.5);
|
||||||
|
public final DoubleParameter ORBIT_PHASE = new DoubleParameter("orbitPhase", "PARTICLE_PARAM_ORBIT_PHASE", 0, 0, 345, 15);
|
||||||
|
|
||||||
|
// A circle whose centre itself moves on a circle around the player
|
||||||
|
public final ParticleDesign ORBIT = new ParticleDesign("orbit", Material.ENDER_PEARL, "PARTICLE_DESIGN_ORBIT", DesignVisibility.INTERNAL, ParticleRequirement.NO_REQUIRMENT,
|
||||||
|
ORBIT_RADIUS, ORBIT_SPEED, ORBIT_PHASE,
|
||||||
|
Designs.CIRCLE_RADIUS, Designs.CIRCLE_SPEED, Designs.CIRCLE_PHASE, Designs.CIRCLE_RISE, Designs.CIRCLE_RISE_SPEED, Designs.CIRCLE_BOUNCE, Designs.CIRCLE_INVERTED) {
|
||||||
|
@Override
|
||||||
|
public ParticleElement build(ParameterValues values, ParticleElement particle) {
|
||||||
|
double rise = values.get(Designs.CIRCLE_RISE);
|
||||||
|
if (rise > 0) {
|
||||||
|
particle = new YOffset(particle, values.get(Designs.CIRCLE_RISE_SPEED), 0, rise, values.get(Designs.CIRCLE_INVERTED), values.get(Designs.CIRCLE_BOUNCE));
|
||||||
|
}
|
||||||
|
ParticleElement inner = new Circle(particle, values.get(Designs.CIRCLE_RADIUS), values.get(Designs.CIRCLE_SPEED), values.get(Designs.CIRCLE_PHASE));
|
||||||
|
return new Circle(inner, values.get(ORBIT_RADIUS), values.get(ORBIT_SPEED), values.get(ORBIT_PHASE));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
public List<ParticleDesign> all() {
|
||||||
|
return List.of(PULSE_SHIMMER, HEART_BEAT, ORBIT);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
/*
|
||||||
|
* This file is a part of the SteamWar software.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2026 SteamWar.de-Serverteam
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program 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 Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.steamwar.lobby.particle.design;
|
||||||
|
|
||||||
|
import com.google.gson.JsonElement;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
public abstract class Parameter<T> {
|
||||||
|
|
||||||
|
private final String key;
|
||||||
|
private final String nameKey;
|
||||||
|
private final T defaultValue;
|
||||||
|
|
||||||
|
protected Parameter(String key, String nameKey, T defaultValue) {
|
||||||
|
this.key = key;
|
||||||
|
this.nameKey = nameKey;
|
||||||
|
this.defaultValue = defaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract JsonElement toJson(T value);
|
||||||
|
|
||||||
|
// Returns null when the stored value cannot be used
|
||||||
|
protected abstract T read(JsonElement json);
|
||||||
|
|
||||||
|
public T fromJson(JsonElement json) {
|
||||||
|
if (json == null || json.isJsonNull()) {
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
T value = read(json);
|
||||||
|
return value == null ? defaultValue : validate(value);
|
||||||
|
} catch (RuntimeException e) {
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public T validate(T value) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,76 @@
|
|||||||
|
/*
|
||||||
|
* This file is a part of the SteamWar software.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2026 SteamWar.de-Serverteam
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program 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 Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.steamwar.lobby.particle.design;
|
||||||
|
|
||||||
|
import com.google.gson.JsonObject;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class ParameterValues {
|
||||||
|
|
||||||
|
private final Map<String, Object> values = new HashMap<>();
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public <T> T get(Parameter<T> parameter) {
|
||||||
|
Object value = values.get(parameter.getKey());
|
||||||
|
return value == null ? parameter.getDefaultValue() : (T) value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T> ParameterValues set(Parameter<T> parameter, T value) {
|
||||||
|
values.put(parameter.getKey(), parameter.validate(value));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Skips the builder's limits; only for presets, which are never stored
|
||||||
|
public <T> ParameterValues setUnchecked(Parameter<T> parameter, T value) {
|
||||||
|
values.put(parameter.getKey(), value);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Puts back values saved with copy()
|
||||||
|
public void restore(ParameterValues saved) {
|
||||||
|
values.clear();
|
||||||
|
values.putAll(saved.values);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ParameterValues copy() {
|
||||||
|
ParameterValues copy = new ParameterValues();
|
||||||
|
copy.values.putAll(values);
|
||||||
|
return copy;
|
||||||
|
}
|
||||||
|
|
||||||
|
public JsonObject toJson(List<Parameter<?>> parameters) {
|
||||||
|
JsonObject json = new JsonObject();
|
||||||
|
parameters.forEach(parameter -> write(json, parameter));
|
||||||
|
return json;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ParameterValues fromJson(JsonObject json, List<Parameter<?>> parameters) {
|
||||||
|
ParameterValues values = new ParameterValues();
|
||||||
|
parameters.forEach(parameter -> values.values.put(parameter.getKey(), parameter.fromJson(json.get(parameter.getKey()))));
|
||||||
|
return values;
|
||||||
|
}
|
||||||
|
|
||||||
|
private <T> void write(JsonObject json, Parameter<T> parameter) {
|
||||||
|
json.add(parameter.getKey(), parameter.toJson(get(parameter)));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,88 @@
|
|||||||
|
/*
|
||||||
|
* This file is a part of the SteamWar software.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2026 SteamWar.de-Serverteam
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program 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 Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.steamwar.lobby.particle.design;
|
||||||
|
|
||||||
|
import de.steamwar.lobby.particle.ParticleElement;
|
||||||
|
import de.steamwar.lobby.particle.ParticleRequirement;
|
||||||
|
import lombok.Getter;
|
||||||
|
import org.bukkit.Material;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
public abstract class ParticleDesign {
|
||||||
|
|
||||||
|
private final String id;
|
||||||
|
private final Material icon;
|
||||||
|
private final String nameKey;
|
||||||
|
private final DesignVisibility visibility;
|
||||||
|
private final ParticleRequirement requirement;
|
||||||
|
private final List<Parameter<?>> parameters;
|
||||||
|
|
||||||
|
protected ParticleDesign(String id, Material icon, String nameKey, DesignVisibility visibility, ParticleRequirement requirement, Parameter<?>... parameters) {
|
||||||
|
this.id = id;
|
||||||
|
this.icon = icon;
|
||||||
|
this.nameKey = nameKey;
|
||||||
|
this.visibility = visibility;
|
||||||
|
this.requirement = requirement;
|
||||||
|
this.parameters = List.of(parameters);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected ParticleDesign(String id, Material icon, String nameKey, Parameter<?>... parameters) {
|
||||||
|
this(id, icon, nameKey, DesignVisibility.BASIC, ParticleRequirement.NO_REQUIRMENT, parameters);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wraps the entry's particle into this design's shape
|
||||||
|
public abstract ParticleElement build(ParameterValues values, ParticleElement particle);
|
||||||
|
|
||||||
|
// Number of particle positions this design spawns per tick
|
||||||
|
public int points(ParameterValues values) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// False for designs with a fixed look that ignore the entry's particle type, count, spread, speed and colours
|
||||||
|
public boolean usesEntryParticle() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Whether the entry editor shows this design setting; some only matter for some values of others
|
||||||
|
public boolean showsSetting(Parameter<?> parameter, ParameterValues values) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// True when the design decides how many particles each point spawns, so the amount setting is hidden and counts as 1
|
||||||
|
public boolean replacesAmount(ParameterValues values) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Whether the design must be drawn every tick for a moment after a jump, whatever its interval; these extra
|
||||||
|
// redraws do not count towards the budget
|
||||||
|
public boolean redrawsAfterJump(ParameterValues values) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Called after a setting changed, to adjust others that depend on it
|
||||||
|
public void adjust(Parameter<?> changed, ParameterValues values) {
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sets the conditions and timing a new entry of this design starts with
|
||||||
|
public void applyDefaults(ParameterValues values) {
|
||||||
|
}
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user