forked from SteamWar/SteamWar
Unify SchematicType loading
This commit is contained in:
@@ -0,0 +1,828 @@
|
||||
/*
|
||||
* 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.data;
|
||||
|
||||
import de.steamwar.sql.SchematicType;
|
||||
import lombok.ToString;
|
||||
|
||||
import java.io.File;
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.*;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@ToString
|
||||
public final class GameModeConfig<M, ST, W> {
|
||||
|
||||
public static final Function<String, String> ToString = Function.identity();
|
||||
public static final Function<String, SchematicType> ToSchematicType = SchematicType::fromDB;
|
||||
public static final Function<File, String> ToStaticWarGear = __ -> "WarGear";
|
||||
public static final Function<File, String> ToInternalName = file -> file.getName().replace(".yml", "");
|
||||
public static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("dd.MM.yyyy HH:mm");
|
||||
private static final Random random = new Random();
|
||||
|
||||
public final boolean loaded;
|
||||
public final File configFile;
|
||||
|
||||
public final Server Server;
|
||||
|
||||
/**
|
||||
* Submission deadline for schematics in 'dd.MM.yyyy HH:mm' format
|
||||
* @implSpec {@code null} by default
|
||||
*/
|
||||
public final Date Deadline;
|
||||
|
||||
/**
|
||||
* The questions that have to be answered to accept the schematic
|
||||
*
|
||||
* @implSpec Disables check schem type if missing
|
||||
*/
|
||||
public final List<String> CheckQuestions;
|
||||
public final Times Times;
|
||||
public final Arena Arena;
|
||||
public final Schematic<M, ST> Schematic;
|
||||
|
||||
/**
|
||||
* The name of the game mode presented to the players
|
||||
*
|
||||
* @implSpec {@code YMLWrapper.getDefaultGameName()} by default
|
||||
*/
|
||||
public final String GameName;
|
||||
|
||||
/**
|
||||
* The months this game mode should be active and playable<br/>
|
||||
* The empty List means all of them
|
||||
*
|
||||
* @implNote 1 is January - 12 is December
|
||||
*/
|
||||
public final List<Integer> ActiveMonths;
|
||||
|
||||
/**
|
||||
* The prefix used for team chats
|
||||
*
|
||||
* @implSpec {@code +} by default
|
||||
*/
|
||||
public final String TeamChatPrefix;
|
||||
public final Blue Blue;
|
||||
public final Red Red;
|
||||
|
||||
/**
|
||||
* The list of active win conditions
|
||||
*/
|
||||
public final List<W> WinConditions;
|
||||
public final WinConditionParams<M> WinConditionParams;
|
||||
public final Kits<M> Kits;
|
||||
|
||||
/**
|
||||
* A list of integers containing the waiting time of this enter stage in the fight
|
||||
*/
|
||||
public final List<Integer> EnterStages;
|
||||
public final Techhider<M> Techhider;
|
||||
|
||||
public GameModeConfig(File file, Function<String, M> materialMapper, Function<String, ST> schematicTypeMapper, Function<String, W> winconditionMapper, Function<File, String> defaultGameName) {
|
||||
YMLWrapper<M, ST, W> loader = new YMLWrapper<>(file, materialMapper, schematicTypeMapper, winconditionMapper);
|
||||
|
||||
configFile = file;
|
||||
loaded = loader.canLoad();
|
||||
Server = new Server(loader.with("Server"));
|
||||
|
||||
String deadlineString = loader.getString("Deadline", null);
|
||||
if (deadlineString != null) {
|
||||
Date Deadline = null;
|
||||
try {
|
||||
Deadline = DATE_FORMAT.parse(deadlineString);
|
||||
} catch (ParseException e) {
|
||||
Deadline = null;
|
||||
}
|
||||
this.Deadline = Deadline;
|
||||
} else {
|
||||
Deadline = null;
|
||||
}
|
||||
|
||||
CheckQuestions = loader.getStringList("CheckQuestions");
|
||||
Times = new Times(loader.with("Times"));
|
||||
// Arena would be here to be in config order but needs Schematic.Size and EnterStages loaded afterwards
|
||||
Schematic = new Schematic<>(loader.with("Schematic"));
|
||||
GameName = loader.getString("GameName", defaultGameName.apply(file));
|
||||
ActiveMonths = loader.getIntList("ActiveMonths");
|
||||
TeamChatPrefix = loader.getString("TeamChatPrefix", "+");
|
||||
Blue = new Blue(loader.with("Blue"));
|
||||
Red = new Red(loader.with("Red"));
|
||||
WinConditions = Collections.unmodifiableList(loader.getStringList("WinConditions").stream().map(loader.winconditionMapper).collect(Collectors.toList()));
|
||||
WinConditionParams = new WinConditionParams<>(loader.with("WinConditionParams"));
|
||||
Kits = new Kits<>(loader.with("Kits"));
|
||||
EnterStages = loader.getIntList("EnterStages");
|
||||
Techhider = new Techhider<>(loader.with("Techhider"));
|
||||
|
||||
Arena = new Arena(loader.with("Arena"), Schematic.Size, EnterStages);
|
||||
}
|
||||
|
||||
@ToString
|
||||
public static final class Server {
|
||||
|
||||
public final boolean loaded;
|
||||
|
||||
/**
|
||||
* Base server folder
|
||||
*/
|
||||
public final String Folder;
|
||||
|
||||
/**
|
||||
* Server java archive
|
||||
*/
|
||||
public final String ServerJar;
|
||||
|
||||
/**
|
||||
* Available arenas
|
||||
*/
|
||||
public final List<String> Maps;
|
||||
|
||||
/**
|
||||
* Names to address the game mode in the chat interface
|
||||
*/
|
||||
public final List<String> ChatNames;
|
||||
|
||||
/**
|
||||
* If the Server is a Spigot server
|
||||
*
|
||||
* @implSpec {@code false} by default
|
||||
*/
|
||||
public final boolean Spigot;
|
||||
|
||||
/**
|
||||
* If the game mode should be marked as a historic game mode
|
||||
*
|
||||
* @implSpec {@code false} by default
|
||||
*/
|
||||
public final boolean Historic;
|
||||
|
||||
/**
|
||||
* If ranked matches should be available for the game mode
|
||||
*
|
||||
* @implSpec {@code false} by default
|
||||
*/
|
||||
public final boolean Ranked;
|
||||
|
||||
private Server(YMLWrapper loader) {
|
||||
loaded = loader.canLoad();
|
||||
Folder = loader.getString("Folder", null);
|
||||
ServerJar = loader.getString("ServerJar", null);
|
||||
Maps = loader.getStringList("Maps");
|
||||
ChatNames = loader.getStringList("ChatNames");
|
||||
Spigot = loader.getBoolean("Spigot", false);
|
||||
Historic = loader.getBoolean("Historic", false);
|
||||
Ranked = loader.getBoolean("Ranked", false);
|
||||
}
|
||||
}
|
||||
|
||||
@ToString
|
||||
public static final class Times {
|
||||
public final boolean loaded;
|
||||
|
||||
/**
|
||||
* Time in seconds the server stops after starting if nobody joins
|
||||
*
|
||||
* @implSpec {@code 300} by default
|
||||
*/
|
||||
public final int NoPlayersOnlineDuration;
|
||||
|
||||
/**
|
||||
* Time in seconds the team leaders have to choose their schematic
|
||||
*
|
||||
* @implSpec {@code 120} by default
|
||||
*/
|
||||
public final int PreSchemPasteDuration;
|
||||
|
||||
/**
|
||||
* Time in seconds for preparing
|
||||
*
|
||||
* @implSpec {@code 300} by default
|
||||
*/
|
||||
public final int SetupDuration;
|
||||
|
||||
/**
|
||||
* Time in seconds the final countdown is long
|
||||
*
|
||||
* @implSpec {@code 30} by default
|
||||
*/
|
||||
public final int PreFightDuration;
|
||||
|
||||
/**
|
||||
* Time in seconds to spectate the arena after the fight
|
||||
*
|
||||
* @implSpec {@code 30} by default
|
||||
*/
|
||||
public final int SpectatorDuration;
|
||||
|
||||
private Times(YMLWrapper loader) {
|
||||
loaded = loader.canLoad();
|
||||
NoPlayersOnlineDuration = loader.getInt("NoPlayersOnlineDuration", 300);
|
||||
PreSchemPasteDuration = loader.getInt("PreSchemPasteDuration", 120);
|
||||
SetupDuration = loader.getInt("SetupDuration", 300);
|
||||
PreFightDuration = loader.getInt("PreFightDuration", 30);
|
||||
SpectatorDuration = loader.getInt("SpectatorDuration", 60);
|
||||
}
|
||||
}
|
||||
|
||||
@ToString
|
||||
public static final class Arena {
|
||||
|
||||
public final boolean loaded;
|
||||
|
||||
/**
|
||||
* The amount of blocks the schematics should be pasted under the surface
|
||||
*
|
||||
* @implSpec {@code 0} by default
|
||||
*/
|
||||
public final int WaterDepth;
|
||||
|
||||
/**
|
||||
* The outer border of the arena, measured in blocks around the schematic areas
|
||||
*/
|
||||
public final Schem2Border Schem2Border;
|
||||
|
||||
/**
|
||||
* The offset the teams spawn relative to the center of their area
|
||||
*/
|
||||
public final SpawnOffset SpawnOffset;
|
||||
|
||||
/**
|
||||
* The size of the team areas are expanded around the schematics
|
||||
*
|
||||
* @implSpec {@code 12} by default
|
||||
*/
|
||||
public final int BorderFromSchematic;
|
||||
|
||||
/**
|
||||
* If ground walkable, teams can walk below the lower arena border during setup
|
||||
*
|
||||
* @implSpec {@code true} by default
|
||||
*/
|
||||
public final boolean GroundWalkable;
|
||||
|
||||
/**
|
||||
* Disable snow and ice melting
|
||||
*
|
||||
* @implSpec {@code false} by default
|
||||
*/
|
||||
public final boolean DisableSnowMelt;
|
||||
|
||||
/**
|
||||
* Allow leaving the arena area as spectator
|
||||
*
|
||||
* @implSpec {@code false} by default
|
||||
*/
|
||||
public final boolean Leaveable;
|
||||
|
||||
/**
|
||||
* Allow missiles to fly to the enemy and not stop at the schem border.
|
||||
*
|
||||
* @implSpec {@code !EnterStages.isEmpty()} by default
|
||||
*/
|
||||
public final boolean AllowMissiles;
|
||||
|
||||
/**
|
||||
* Denotes that there is no floor for this GameMode
|
||||
*
|
||||
* @implSpec {@code false} by default
|
||||
*/
|
||||
public final boolean NoFloor;
|
||||
|
||||
private Arena(YMLWrapper loader, Schematic.Size Size, List<Integer> EnterStages) {
|
||||
loaded = loader.canLoad();
|
||||
WaterDepth = loader.getInt("WaterDepth", 0);
|
||||
Schem2Border = new Schem2Border(loader.with("Schem2Border"));
|
||||
SpawnOffset = new SpawnOffset(loader.with("SpawnOffset"), Size);
|
||||
BorderFromSchematic = loader.getInt("BorderFromSchematic", 21);
|
||||
GroundWalkable = loader.getBoolean("GroundWalkable", true);
|
||||
DisableSnowMelt = loader.getBoolean("DisableSnowMelt", false);
|
||||
Leaveable = loader.getBoolean("Leaveable", false);
|
||||
AllowMissiles = loader.getBoolean("AllowMissiles", !EnterStages.isEmpty());
|
||||
NoFloor = loader.getBoolean("NoFloor", false);
|
||||
}
|
||||
|
||||
@ToString
|
||||
public static final class Schem2Border {
|
||||
|
||||
public final boolean loaded;
|
||||
|
||||
/**
|
||||
* @implSpec {@code 24} by default
|
||||
*/
|
||||
public final int x;
|
||||
|
||||
/**
|
||||
* @implSpec {@code 24} by default
|
||||
*/
|
||||
public final int z;
|
||||
|
||||
private Schem2Border(YMLWrapper loader) {
|
||||
loaded = loader.canLoad();
|
||||
x = loader.getInt("x", 24);
|
||||
z = loader.getInt("z", 24);
|
||||
}
|
||||
}
|
||||
|
||||
@ToString
|
||||
public static final class SpawnOffset {
|
||||
|
||||
public final boolean loaded;
|
||||
|
||||
/**
|
||||
* @implSpec {@code 0} by default
|
||||
*/
|
||||
public final double x;
|
||||
|
||||
/**
|
||||
* @implSpec {@code Schematic.Size.y} by default
|
||||
*/
|
||||
public final double y;
|
||||
|
||||
/**
|
||||
* @implSpec {@code 0} by default
|
||||
*/
|
||||
public final double z;
|
||||
|
||||
private SpawnOffset(YMLWrapper loader, Schematic.Size Size) {
|
||||
loaded = loader.canLoad();
|
||||
x = loader.getDouble("x", 0);
|
||||
y = loader.getDouble("y", Size.y);
|
||||
z = loader.getDouble("z", 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ToString
|
||||
public static final class Schematic<M, ST> {
|
||||
|
||||
public final boolean loaded;
|
||||
|
||||
/**
|
||||
* The size of the schematics
|
||||
*/
|
||||
public final Size Size;
|
||||
|
||||
/**
|
||||
* Used for GameModes with a technic area
|
||||
*/
|
||||
public final Inset Inset;
|
||||
|
||||
/**
|
||||
* The schematic type that can be chosen in this arena
|
||||
*
|
||||
* @implSpec {@code Normal} by default
|
||||
*/
|
||||
public final ST Type;
|
||||
|
||||
/**
|
||||
* The schematic types that are also allowed to be chosen in this arena
|
||||
*/
|
||||
public final List<ST> SubTypes;
|
||||
|
||||
/**
|
||||
* Shortcut of the schematic type
|
||||
*
|
||||
* @implSpec {@code ""} by default
|
||||
*/
|
||||
public final String Shortcut;
|
||||
|
||||
/**
|
||||
* Spigot (1.8) material for GUIs
|
||||
*
|
||||
* @implSpec {@code STONE_BUTTON} by default
|
||||
*/
|
||||
public final M Material;
|
||||
|
||||
/**
|
||||
* Manual check of schematic necessary
|
||||
*
|
||||
* @implSpec {@code true} by default
|
||||
*/
|
||||
public final boolean ManualCheck;
|
||||
|
||||
/**
|
||||
* If the schematics should be rotated during pasting
|
||||
*
|
||||
* @implSpec {@code true} by default
|
||||
*/
|
||||
public final boolean Rotate;
|
||||
|
||||
/**
|
||||
* If the schematics should be pasted aligned to the borders instead of centered
|
||||
*
|
||||
* @implSpec {@code false} by default
|
||||
*/
|
||||
public final boolean PasteAligned;
|
||||
|
||||
/**
|
||||
* If only public schematics are allowed
|
||||
*
|
||||
* @implSpec {@code false} by default
|
||||
*/
|
||||
public final boolean OnlyPublicSchematics;
|
||||
|
||||
/**
|
||||
* If the public only force should be completely disabled
|
||||
*
|
||||
* @implSpec {@code false} by default
|
||||
*/
|
||||
public final boolean IgnorePublicOnly;
|
||||
|
||||
/**
|
||||
* If obsidian and bedrock should be replaced during PRE_RUNNING
|
||||
*
|
||||
* @implSpec {@code false} by default
|
||||
*/
|
||||
public final boolean ReplaceObsidianBedrock;
|
||||
|
||||
/**
|
||||
* If the replacement should happen with block updates
|
||||
*
|
||||
* @implSpec {@code false} by default
|
||||
*/
|
||||
public final boolean ReplaceWithBlockupdates;
|
||||
|
||||
/**
|
||||
* If the schematic perparation arena mode is time limited
|
||||
*
|
||||
* @implSpec {@code false} by default
|
||||
*/
|
||||
public final boolean UnlimitedPrepare;
|
||||
|
||||
/**
|
||||
* Maximal amount of blocks allowed in the schematic
|
||||
*
|
||||
* @implSpec {@code 0} by default
|
||||
*/
|
||||
public final int MaxBlocks;
|
||||
|
||||
/**
|
||||
* Maximal amount of items per dispenser
|
||||
*
|
||||
* @implSpec {@code 128} by default
|
||||
*/
|
||||
public final int MaxDispenserItems;
|
||||
|
||||
/**
|
||||
* Maximal blast resistance for the design blocks
|
||||
*
|
||||
* @implSpec {@code Double.MAX_VALUE} by default
|
||||
*/
|
||||
public final double MaxDesignBlastResistance;
|
||||
|
||||
/**
|
||||
* List of limited material (combinations)<br/>
|
||||
* List contains tags Amount (integer) and Materials (List of material names in Spigot 1.12 AND Spigot 1.15 format)
|
||||
*/
|
||||
public final Map<Set<M>, Integer> Limited;
|
||||
|
||||
private Schematic(YMLWrapper<M, ST, ?> loader) {
|
||||
loaded = loader.canLoad();
|
||||
Size = new Size(loader.with("Size"));
|
||||
Inset = new Inset(loader.with("Inset"));
|
||||
Type = loader.getSchematicType("Type", "Normal");
|
||||
SubTypes = loader.getSchematicTypeList("SubTypes");
|
||||
Shortcut = loader.getString("Shortcut", "");
|
||||
Material = loader.getMaterial("Material", "STONE_BUTTON");
|
||||
ManualCheck = loader.getBoolean("ManualCheck", true);
|
||||
Rotate = loader.getBoolean("Rotate", true);
|
||||
PasteAligned = loader.getBoolean("PasteAligned", false);
|
||||
OnlyPublicSchematics = loader.getBoolean("OnlyPublicSchematics", false);
|
||||
IgnorePublicOnly = loader.getBoolean("IgnorePublicOnly", false);
|
||||
ReplaceObsidianBedrock = loader.getBoolean("ReplaceObsidianBedrock", false);
|
||||
ReplaceWithBlockupdates = loader.getBoolean("ReplaceWithBlockupdates", false);
|
||||
UnlimitedPrepare = loader.getBoolean("UnlimitedPrepare", false);
|
||||
MaxBlocks = loader.getInt("MaxBlocks", 0);
|
||||
MaxDispenserItems = loader.getInt("MaxDispenserItems", 128);
|
||||
MaxDesignBlastResistance = loader.getDouble("MaxDesignBlastResistance", Double.MAX_VALUE);
|
||||
|
||||
Map<Set<M>, Integer> Limited = new HashMap<>();
|
||||
for (Map<?, ?> entry : loader.getMapList("Limited")) {
|
||||
int amount = (Integer) entry.get("Amount");
|
||||
Set<String> materials = new HashSet<>((List<String>) entry.get("Materials"));
|
||||
if (amount == 0) {
|
||||
materials.forEach(material -> {
|
||||
Limited.put(Collections.singleton(loader.materialMapper.apply(material.toUpperCase())), 0);
|
||||
});
|
||||
} else {
|
||||
Limited.put(Collections.unmodifiableSet(materials.stream().map(String::toUpperCase).map(loader.materialMapper).collect(Collectors.toSet())), amount);
|
||||
}
|
||||
}
|
||||
this.Limited = Collections.unmodifiableMap(Limited);
|
||||
}
|
||||
|
||||
@ToString
|
||||
public static final class Size {
|
||||
|
||||
public final boolean loaded;
|
||||
|
||||
/**
|
||||
* @implSpec {@code 0} by default
|
||||
*/
|
||||
public final int x;
|
||||
|
||||
/**
|
||||
* @implSpec {@code 0} by default
|
||||
*/
|
||||
public final int y;
|
||||
|
||||
/**
|
||||
* @implSpec {@code 0} by default
|
||||
*/
|
||||
public final int z;
|
||||
|
||||
private Size(YMLWrapper loader) {
|
||||
loaded = loader.canLoad();
|
||||
x = loader.getInt("x", 0);
|
||||
y = loader.getInt("y", 0);
|
||||
z = loader.getInt("z", 0);
|
||||
}
|
||||
}
|
||||
|
||||
@ToString
|
||||
public static final class Inset {
|
||||
|
||||
public final boolean loaded;
|
||||
|
||||
/**
|
||||
* @implSpec {@code 0} by default
|
||||
*/
|
||||
public final int x;
|
||||
|
||||
/**
|
||||
* @implSpec {@code 0} by default
|
||||
*/
|
||||
public final int z;
|
||||
|
||||
/**
|
||||
* @implSpec {@code 0} by default
|
||||
*/
|
||||
public final int top;
|
||||
|
||||
/**
|
||||
* @implSpec {@code 0} by default
|
||||
*/
|
||||
public final int bottom;
|
||||
|
||||
private Inset(YMLWrapper loader) {
|
||||
loaded = loader.canLoad();
|
||||
x = loader.getInt("x", 0);
|
||||
z = loader.getInt("z", 0);
|
||||
top = loader.getInt("top", 0);
|
||||
bottom = loader.getInt("bottom", 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ToString
|
||||
public static final class Blue {
|
||||
|
||||
public final boolean loaded;
|
||||
|
||||
/**
|
||||
* @implSpec {@code Blau} by default
|
||||
*/
|
||||
public final String Name;
|
||||
|
||||
/**
|
||||
* @implSpec {@code §3} by default
|
||||
*/
|
||||
public final String Prefix;
|
||||
|
||||
private Blue(YMLWrapper loader) {
|
||||
loaded = loader.canLoad();
|
||||
Name = loader.getString("Name", "Blau");
|
||||
Prefix = loader.getString("Prefix", "§3");
|
||||
}
|
||||
}
|
||||
|
||||
@ToString
|
||||
public static final class Red {
|
||||
|
||||
public final boolean loaded;
|
||||
|
||||
/**
|
||||
* @implSpec {@code Rot} by default
|
||||
*/
|
||||
public final String Name;
|
||||
|
||||
/**
|
||||
* @implSpec {@code §c} by default
|
||||
*/
|
||||
public final String Prefix;
|
||||
|
||||
private Red(YMLWrapper loader) {
|
||||
loaded = loader.canLoad();
|
||||
Name = loader.getString("Name", "Rot");
|
||||
Prefix = loader.getString("Prefix", "§c");
|
||||
}
|
||||
}
|
||||
|
||||
@ToString
|
||||
public static final class WinConditionParams<M> {
|
||||
|
||||
public final boolean loaded;
|
||||
|
||||
/**
|
||||
* The time of any of the timeout win conditions in seconds
|
||||
*
|
||||
* @implSpec {@code 1200} by default
|
||||
*/
|
||||
public final int TimeoutTime;
|
||||
|
||||
/**
|
||||
* The percentage when any of the percent win conditions limits or triggers a win
|
||||
*
|
||||
* @implSpec {@code 7.0} by default
|
||||
*/
|
||||
public final double PercentWin;
|
||||
|
||||
/**
|
||||
* Does the percentage still change after the start of the enter phase
|
||||
*
|
||||
* @implSpec {@code true} by default
|
||||
*/
|
||||
public final boolean PercentEntern;
|
||||
|
||||
/**
|
||||
* Is Blocks a whitelist (true) or blacklist (false)
|
||||
*
|
||||
* @implSpec {@code false} by default
|
||||
*/
|
||||
public final boolean BlocksWhitelist;
|
||||
|
||||
/**
|
||||
* Special Blocks (Valid spigot material values) used by the percent win conditions
|
||||
*/
|
||||
public final List<M> Blocks;
|
||||
|
||||
/**
|
||||
* Time for being declared TechKo without a shot given.
|
||||
*
|
||||
* @implSpec {@code 90} by default
|
||||
*/
|
||||
public final int TechKoTime;
|
||||
|
||||
private WinConditionParams(YMLWrapper<M, ?, ?> loader) {
|
||||
loaded = loader.canLoad();
|
||||
TimeoutTime = loader.getInt("TimeoutTime", 1200);
|
||||
PercentWin = loader.getDouble("PercentWin", 7.0);
|
||||
PercentEntern = loader.getBoolean("PercentEntern", true);
|
||||
BlocksWhitelist = loader.getBoolean("BlocksWhitelist", false);
|
||||
Blocks = loader.getMaterialList("Blocks");
|
||||
TechKoTime = loader.getInt("TechKoTime", 90);
|
||||
}
|
||||
}
|
||||
|
||||
@ToString
|
||||
public static final class Kits<M> {
|
||||
|
||||
public final boolean loaded;
|
||||
|
||||
/**
|
||||
* The kit file for this configuration
|
||||
*
|
||||
* @implSpec {@code kits.yml} by default
|
||||
*/
|
||||
public final String File;
|
||||
|
||||
/**
|
||||
* The default kit for team members
|
||||
*
|
||||
* @implSpec {@code default} by default
|
||||
*/
|
||||
public final String MemberDefault;
|
||||
|
||||
/**
|
||||
* The default kit for team leaders
|
||||
*
|
||||
* @implSpec {@code default} by default
|
||||
*/
|
||||
public final String LeaderDefault;
|
||||
|
||||
/**
|
||||
* If the personal kit system is active
|
||||
*
|
||||
* @implSpec {@code false} by default
|
||||
*/
|
||||
public final boolean PersonalKits;
|
||||
|
||||
/**
|
||||
* Items (Valid spigot material values) that are not allowed in the personal kit
|
||||
*/
|
||||
public final List<M> ForbiddenItems;
|
||||
|
||||
private Kits(YMLWrapper<M, ?, ?> loader) {
|
||||
loaded = loader.canLoad();
|
||||
File = loader.getString("File", "kits.yml");
|
||||
MemberDefault = loader.getString("MemberDefault", "default");
|
||||
LeaderDefault = loader.getString("LeaderDefault", "default");
|
||||
PersonalKits = loader.getBoolean("PersonalKits", false);
|
||||
ForbiddenItems = loader.getMaterialList("ForbiddenItems");
|
||||
}
|
||||
}
|
||||
|
||||
@ToString
|
||||
public static final class Techhider<M> {
|
||||
|
||||
public final boolean loaded;
|
||||
|
||||
/**
|
||||
* Activates the tech hider
|
||||
*
|
||||
* @implSpec {@code false} by default
|
||||
*/
|
||||
public final boolean Active;
|
||||
|
||||
/**
|
||||
* Which block the tech hider replaces to.
|
||||
*
|
||||
* @implSpec {@code end_stone} by default
|
||||
*/
|
||||
public final M ObfuscateWith;
|
||||
|
||||
/**
|
||||
* A list of all hidden blocks. "water" results in the hiding of all waterlogged blocks as well.
|
||||
*/
|
||||
public final Set<M> HiddenBlocks;
|
||||
|
||||
/**
|
||||
* The block entity contents that are hidden (here with minecraft:nametag)
|
||||
*/
|
||||
public final Set<String> HiddenBlockEntities;
|
||||
|
||||
private Techhider(YMLWrapper<M, ?, ?> loader) {
|
||||
loaded = loader.canLoad();
|
||||
Active = loader.getBoolean("Active", false);
|
||||
ObfuscateWith = loader.getMaterial("ObfuscateWith", "end_stone");
|
||||
HiddenBlocks = Collections.unmodifiableSet(new HashSet<>(loader.getMaterialList("HiddenBlocks")));
|
||||
HiddenBlockEntities = Collections.unmodifiableSet(new HashSet<>(loader.getStringList("HiddenBlockEntities")));
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isAfterDeadline() {
|
||||
return Deadline != null && Deadline.before(Date.from(Instant.now()));
|
||||
}
|
||||
|
||||
public String hasMap(String map) {
|
||||
for (String m : Server.Maps) {
|
||||
if (m.equalsIgnoreCase(map))
|
||||
return m;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public String getRandomMap() {
|
||||
return Server.Maps.get(random.nextInt(Server.Maps.size()));
|
||||
}
|
||||
|
||||
public String convertToRealMapName(String map) {
|
||||
for (String m : Server.Maps) {
|
||||
if (m.equalsIgnoreCase(map))
|
||||
return m;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public String getChatName() {
|
||||
return Server.ChatNames.get(0);
|
||||
}
|
||||
|
||||
public boolean isActive() {
|
||||
if (Server.ChatNames.isEmpty()) return false;
|
||||
if (ActiveMonths.isEmpty()) return true;
|
||||
return ActiveMonths.contains(LocalDateTime.now().getMonth().getValue());
|
||||
}
|
||||
|
||||
public String getSchemTypeOrInternalName() {
|
||||
if (Schematic.loaded) {
|
||||
ST type = Schematic.Type;
|
||||
if (type instanceof SchematicType) {
|
||||
return ((SchematicType) type).toDB();
|
||||
} else if (type instanceof String) {
|
||||
return (String) type;
|
||||
}
|
||||
}
|
||||
return configFile.getName().replace(".yml", "");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,164 @@
|
||||
/*
|
||||
* 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.data;
|
||||
|
||||
import org.yaml.snakeyaml.Yaml;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileReader;
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
final class YMLWrapper<M, ST, W> {
|
||||
|
||||
final Function<String, M> materialMapper;
|
||||
final Function<String, ST> schematicTypeMapper;
|
||||
final Function<String, W> winconditionMapper;
|
||||
|
||||
private final boolean canLoad;
|
||||
private final Map<String, Object> document;
|
||||
|
||||
YMLWrapper(File file, Function<String, M> materialMapper, Function<String, ST> schematicTypeMapper, Function<String, W> winconditionMapper) {
|
||||
this.materialMapper = materialMapper;
|
||||
this.schematicTypeMapper = schematicTypeMapper;
|
||||
this.winconditionMapper = winconditionMapper;
|
||||
|
||||
Yaml yaml = new Yaml();
|
||||
Map<String, Object> document = Collections.emptyMap();
|
||||
boolean canLoad = false;
|
||||
if (file != null && file.exists() && file.isFile()) {
|
||||
try {
|
||||
document = yaml.load(new FileReader(file));
|
||||
canLoad = true;
|
||||
} catch (IOException e) {
|
||||
// Ignore
|
||||
}
|
||||
}
|
||||
this.document = document;
|
||||
this.canLoad = canLoad;
|
||||
}
|
||||
|
||||
private YMLWrapper(boolean canLoad, Map<String, Object> document, Function<String, M> materialMapper, Function<String, ST> schematicTypeMapper, Function<String, W> winconditionMapper) {
|
||||
this.materialMapper = materialMapper;
|
||||
this.schematicTypeMapper = schematicTypeMapper;
|
||||
this.winconditionMapper = winconditionMapper;
|
||||
this.canLoad = canLoad;
|
||||
this.document = document;
|
||||
}
|
||||
|
||||
public boolean canLoad() {
|
||||
return canLoad;
|
||||
}
|
||||
|
||||
public YMLWrapper<M, ST, W> with(String path) {
|
||||
if (document.containsKey(path)) {
|
||||
Object value = document.get(path);
|
||||
if (value instanceof Map) {
|
||||
return new YMLWrapper<>(true, (Map) value, materialMapper, schematicTypeMapper, winconditionMapper);
|
||||
}
|
||||
}
|
||||
return new YMLWrapper<>(false, Collections.emptyMap(), materialMapper, schematicTypeMapper, winconditionMapper);
|
||||
}
|
||||
|
||||
public <T> T get(String path, T defaultValue, Function<Object, T> mapper) {
|
||||
Object value = this.document.get(path);
|
||||
if (value == null) return defaultValue;
|
||||
try {
|
||||
return mapper.apply(value);
|
||||
} catch (ClassCastException e) {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
public String getString(String path, String defaultValue) {
|
||||
return get(path, defaultValue, Objects::toString);
|
||||
}
|
||||
|
||||
public int getInt(String path, int defaultValue) {
|
||||
return get(path, defaultValue, Integer.class::cast);
|
||||
}
|
||||
|
||||
public double getDouble(String path, double defaultValue) {
|
||||
return get(path, defaultValue, Double.class::cast);
|
||||
}
|
||||
|
||||
public boolean getBoolean(String path, boolean defaultValue) {
|
||||
return get(path, defaultValue, Boolean.class::cast);
|
||||
}
|
||||
|
||||
public ST getSchematicType(String path, String defaultValue) {
|
||||
String schematicType = getString(path, defaultValue);
|
||||
return schematicTypeMapper.apply(schematicType);
|
||||
}
|
||||
|
||||
public M getMaterial(String path, String defaultValue) {
|
||||
return materialMapper.apply(getString(path, defaultValue).toUpperCase());
|
||||
}
|
||||
|
||||
public <T> List<T> get(String path, Function<Object, List<T>> mapper) {
|
||||
Object value = this.document.get(path);
|
||||
if (value == null) return Collections.emptyList();
|
||||
try {
|
||||
return Collections.unmodifiableList(mapper.apply(value));
|
||||
} catch (ClassCastException e) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
|
||||
public List<String> getStringList(String path) {
|
||||
return get(path, o -> (List<String>) o);
|
||||
}
|
||||
|
||||
public List<Integer> getIntList(String path) {
|
||||
return get(path, o -> (List<Integer>) o);
|
||||
}
|
||||
|
||||
public List<ST> getSchematicTypeList(String path) {
|
||||
List<String> list = getStringList(path);
|
||||
if (list.isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
} else {
|
||||
return Collections.unmodifiableList(list.stream().map(schematicTypeMapper).collect(Collectors.toList()));
|
||||
}
|
||||
}
|
||||
|
||||
public List<M> getMaterialList(String path) {
|
||||
List<String> list = getStringList(path);
|
||||
if (list.isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
} else {
|
||||
return Collections.unmodifiableList(list.stream().map(String::toUpperCase).map(materialMapper).collect(Collectors.toList()));
|
||||
}
|
||||
}
|
||||
|
||||
public List<Map<?, ?>> getMapList(String path) {
|
||||
Object value = this.document.get(path);
|
||||
if (value instanceof List) {
|
||||
return Collections.unmodifiableList((List<Map<?, ?>>) value);
|
||||
} else {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -20,14 +20,17 @@
|
||||
package de.steamwar.sql;
|
||||
|
||||
import de.steamwar.ImplementationProvider;
|
||||
import de.steamwar.data.GameModeConfig;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.io.File;
|
||||
|
||||
public interface SQLWrapper {
|
||||
SQLWrapper impl = ImplementationProvider.getImpl("de.steamwar.sql.SQLWrapperImpl");
|
||||
|
||||
void loadSchemTypes(List<SchematicType> tmpTypes, Map<String, SchematicType> tmpFromDB);
|
||||
File getSchemTypesFolder();
|
||||
|
||||
default void processSchematicType(GameModeConfig<String, String, String> gameModeConfig, SchematicType type) {
|
||||
}
|
||||
|
||||
void additionalExceptionMetadata(StringBuilder builder);
|
||||
}
|
||||
|
||||
@@ -19,11 +19,14 @@
|
||||
|
||||
package de.steamwar.sql;
|
||||
|
||||
import de.steamwar.data.GameModeConfig;
|
||||
import de.steamwar.sql.internal.SqlTypeMapper;
|
||||
import lombok.Getter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Slf4j
|
||||
public class SchematicType {
|
||||
@@ -41,7 +44,33 @@ public class SchematicType {
|
||||
tmpFromDB.put(Normal.name().toLowerCase(), Normal);
|
||||
|
||||
long time = System.currentTimeMillis();
|
||||
SQLWrapper.impl.loadSchemTypes(tmpTypes, tmpFromDB);
|
||||
File folder = SQLWrapper.impl.getSchemTypesFolder();
|
||||
if (folder.exists()) {
|
||||
for (File configFile : Arrays.stream(folder.listFiles((file, name) -> name.endsWith(".yml") && !name.endsWith(".kits.yml"))).sorted().collect(Collectors.toList())) {
|
||||
GameModeConfig<String, String, String> gameModeConfig = new GameModeConfig<>(configFile, GameModeConfig.ToString, GameModeConfig.ToString, GameModeConfig.ToString, GameModeConfig.ToStaticWarGear);
|
||||
if (!gameModeConfig.Schematic.loaded) continue;
|
||||
String type = gameModeConfig.Schematic.Type;
|
||||
assert type != null;
|
||||
String shortcut = gameModeConfig.Schematic.Shortcut;
|
||||
if (tmpFromDB.containsKey(type.toLowerCase()))
|
||||
continue;
|
||||
|
||||
SchematicType checktype = null;
|
||||
String material = gameModeConfig.Schematic.Material;
|
||||
|
||||
if (!gameModeConfig.CheckQuestions.isEmpty()) {
|
||||
checktype = new SchematicType("C" + type, "C" + shortcut, SchematicType.Type.CHECK_TYPE, null, material, true);
|
||||
tmpTypes.add(checktype);
|
||||
tmpFromDB.put(checktype.toDB(), checktype);
|
||||
}
|
||||
|
||||
SchematicType current = new SchematicType(type, shortcut, gameModeConfig.Server.loaded ? SchematicType.Type.FIGHT_TYPE : SchematicType.Type.NORMAL, checktype, material, gameModeConfig.Deadline, gameModeConfig.Schematic.ManualCheck);
|
||||
tmpTypes.add(current);
|
||||
tmpFromDB.put(type.toLowerCase(), current);
|
||||
|
||||
SQLWrapper.impl.processSchematicType(gameModeConfig, current);
|
||||
}
|
||||
}
|
||||
time = System.currentTimeMillis() - time;
|
||||
log.info("Loaded {} Schematic Types in {}ms", tmpTypes.size(), time);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user