Compare commits

..
Author SHA1 Message Date
YoyoNow a07067725f Improve YoyoNowAI.schematic 2026-04-20 08:51:49 +02:00
YoyoNow 264e541fc7 Improve YoyoNowAI.schematic 2026-04-20 08:31:39 +02:00
YoyoNow 82a59f772e Fix LocalCoordinate, WorldCoordinate
Fix WarMachine
2026-04-18 17:21:53 +02:00
YoyoNow 2c44e50846 Fix Action.MoveAction
Fix NavMesh.isWalkable
2026-04-17 11:13:14 +02:00
YoyoNow f51bce6a69 Add WorldCoordinate and LocalCoordinate to remove mixup
Improve NavMesh
Add Action
2026-04-17 10:13:28 +02:00
YoyoNow bf26dfed56 Fix NavMesh and implement some initial stuff 2026-04-16 19:44:06 +02:00
YoyoNow 395303d04f Add NavMesh 2026-04-16 12:10:33 +02:00
78 changed files with 1377 additions and 1051 deletions
@@ -1,63 +0,0 @@
/*
* 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.dev;
import de.steamwar.bausystem.BauSystem;
import de.steamwar.command.SWCommand;
import de.steamwar.linkage.Linked;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.entity.Player;
import java.io.File;
import java.io.IOException;
@Linked
public class CreateKitCommand extends SWCommand {
public CreateKitCommand() {
super("createkit");
if (!BauSystem.DEV_SERVER) unregister();
}
@Register
public void onCommand(Player player, String name) {
YamlConfiguration yaml = new YamlConfiguration();
yaml.set("Items", player.getInventory().getContents());
yaml.set("Armor", player.getInventory().getArmorContents());
yaml.set("Effects", player.getActivePotionEffects());
yaml.set("LeaderAllowed", true);
yaml.set("MemberAllowed", true);
yaml.set("EnterStage", 0);
yaml.set("TNT", true);
YamlConfiguration kits = new YamlConfiguration();
kits.set("Kits." + name, yaml);
try {
kits.save(new File("new.kits.yaml"));
player.sendMessage("Kit created!");
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
@@ -67,7 +67,11 @@ public class TNTListener implements Listener, ScoreboardElement {
@EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true)
public void onExplode(EntityExplodeEvent event) { public void onExplode(EntityExplodeEvent event) {
explode(event.blockList(), event.getEntity() instanceof TNTPrimed); if (!(event.getEntity() instanceof TNTPrimed)) {
event.blockList().clear();
return;
}
explode(event.blockList(), true);
} }
@Override @Override
@@ -135,7 +135,7 @@ public abstract class ViewFlag {
} }
Location secoundLocation; Location secoundLocation;
if (Math.abs(previousVelocity.getX()) >= Math.abs(previousVelocity.getZ())) { if (previousVelocity.getX() >= previousVelocity.getZ()) {
secoundLocation = previous.getLocation().clone().add(delta.getX(), delta.getY(), 0); secoundLocation = previous.getLocation().clone().add(delta.getX(), delta.getY(), 0);
} else { } else {
secoundLocation = previous.getLocation().clone().add(0, delta.getY(), delta.getZ()); secoundLocation = previous.getLocation().clone().add(0, delta.getY(), delta.getZ());
@@ -198,16 +198,6 @@ public abstract class ViewFlag {
} }
}; };
public static ViewFlag HIGHLIGHT = new ViewFlag(true, false, "highlight", "h") {
@Override
public void modify(REntityServer server, List<TraceEntity> entities) {
for (TraceEntity entity : entities) {
entity.setGlowing(true);
}
}
};
/** /**
* Name of the flag * Name of the flag
*/ */
@@ -28,11 +28,11 @@ import org.jetbrains.exposed.v1.javatime.timestamp
import java.time.Instant import java.time.Instant
object AuditLogTable: IntIdTable("AuditLog", "AuditLogId") { object AuditLogTable: IntIdTable("AuditLog", "AuditLogId") {
val time = timestamp("Time").index() val time = timestamp("Time")
val server = varchar("ServerName", 255).index() val server = varchar("ServerName", 255)
val serverOwner = reference("ServerOwner", SteamwarUserTable).nullable().index() val serverOwner = reference("ServerOwner", SteamwarUserTable).nullable()
val actor = reference("Actor", SteamwarUserTable).index() val actor = reference("Actor", SteamwarUserTable)
val action = enumerationByName("ActionType", 255, AuditLog.Type::class).index() val action = enumerationByName("ActionType", 255, AuditLog.Type::class)
val actionText = text("ActionText") val actionText = text("ActionText")
} }
@@ -33,8 +33,8 @@ import java.sql.Timestamp
import java.time.Instant import java.time.Instant
object BannedUserIPsTable: CompositeIdTable("BannedUserIPs") { object BannedUserIPsTable: CompositeIdTable("BannedUserIPs") {
val userId = reference("UserID", SteamwarUserTable).index() val userId = reference("UserID", SteamwarUserTable)
val timestamp = timestamp("Timestamp").index() val timestamp = timestamp("Timestamp")
val ip = varchar("IP", 45).entityId() val ip = varchar("IP", 45).entityId()
init { init {
@@ -32,8 +32,8 @@ import org.jetbrains.exposed.v1.jdbc.insertIgnore
import java.util.* import java.util.*
object BauweltMemberTable: CompositeIdTable("BauweltMember") { object BauweltMemberTable: CompositeIdTable("BauweltMember") {
val bauweltId = reference("BauweltID", SteamwarUserTable).index() val bauweltId = reference("BauweltID", SteamwarUserTable)
val memberId = reference("MemberID", SteamwarUserTable).index() val memberId = reference("MemberID", SteamwarUserTable)
val build = bool("Build") val build = bool("Build")
val worldEdit = bool("WorldEdit") val worldEdit = bool("WorldEdit")
val world = bool("World") val world = bool("World")
@@ -20,7 +20,6 @@
package de.steamwar.sql package de.steamwar.sql
import de.steamwar.sql.internal.useDb import de.steamwar.sql.internal.useDb
import org.jetbrains.exposed.v1.core.ReferenceOption
import org.jetbrains.exposed.v1.core.SortOrder import org.jetbrains.exposed.v1.core.SortOrder
import org.jetbrains.exposed.v1.core.and import org.jetbrains.exposed.v1.core.and
import org.jetbrains.exposed.v1.core.dao.id.CompositeID import org.jetbrains.exposed.v1.core.dao.id.CompositeID
@@ -35,23 +34,19 @@ import org.jetbrains.exposed.v1.jdbc.insertIgnore
import java.sql.Timestamp import java.sql.Timestamp
object CheckedSchematicTable: CompositeIdTable("CheckedSchematic") { object CheckedSchematicTable: CompositeIdTable("CheckedSchematic") {
val nodeId = optReference("NodeId", SchematicNodeTable, onDelete = ReferenceOption.SET_NULL, onUpdate = ReferenceOption.SET_NULL).index() val nodeId = optReference("NodeId", SchematicNodeTable)
val nodeOwner = reference("NodeOwner", SteamwarUserTable).index() val nodeOwner = reference("NodeOwner", SteamwarUserTable)
val nodeName = varchar("NodeName", 64).entityId().index() val nodeName = varchar("NodeName", 64).entityId()
val validator = reference("Validator", SteamwarUserTable).index() val validator = reference("Validator", SteamwarUserTable)
val startTime = timestamp("StartTime").entityId().index() val startTime = timestamp("StartTime").entityId()
val endTime = timestamp("EndTime") val endTime = timestamp("EndTime")
val declineReason = text("DeclineReason") val declineReason = text("DeclineReason")
val seen = bool("Seen").index() val seen = bool("Seen")
val nodeType = varchar("NodeType", 16) val nodeType = varchar("NodeType", 16)
init { init {
addIdColumn(nodeOwner) addIdColumn(nodeOwner)
addIdColumn(nodeName) addIdColumn(nodeName)
index(false, nodeOwner, endTime)
index(false, startTime, endTime, nodeName)
index(false, seen, nodeOwner, startTime)
} }
} }
+2 -2
View File
@@ -36,8 +36,8 @@ import java.time.Instant
object EventTable : IntIdTable("Event", "EventId") { object EventTable : IntIdTable("Event", "EventId") {
val name = varchar("EventName", 100).uniqueIndex() val name = varchar("EventName", 100).uniqueIndex()
val deadline = timestamp("Deadline") val deadline = timestamp("Deadline")
val start = timestamp("Start").index() val start = timestamp("Start")
val end = timestamp("End").index() val end = timestamp("End")
val maxPlayers = integer("MaximumTeamMembers") val maxPlayers = integer("MaximumTeamMembers")
val schemType = varchar("SchemType", 16).nullable() val schemType = varchar("SchemType", 16).nullable()
val publicsOnly = bool("PublicSchemsOnly") val publicsOnly = bool("PublicSchemsOnly")
@@ -33,17 +33,17 @@ import java.time.Instant
import java.util.* import java.util.*
object EventFightTable : IntIdTable("EventFight", "FightID") { object EventFightTable : IntIdTable("EventFight", "FightID") {
val eventId = reference("EventID", EventTable).index() val eventId = reference("EventID", EventTable)
val startTime = timestamp("StartTime").index() val startTime = timestamp("StartTime")
val gamemode = text("Spielmodus") val gamemode = text("Spielmodus")
val map = text("Map") val map = text("Map")
val groupId = optReference("GroupId", EventGroupTable).index() val groupId = optReference("GroupId", EventGroupTable)
val teamBlue = reference("TeamBlue", TeamTable).index() val teamBlue = reference("TeamBlue", TeamTable)
val teamRed = reference("TeamRed", TeamTable).index() val teamRed = reference("TeamRed", TeamTable)
val spectatePort = integer("SpectatePort").nullable() val spectatePort = integer("SpectatePort").nullable()
val bestOf = integer("BestOf") val bestOf = integer("BestOf")
val ergebnis = integer("Ergebnis") val ergebnis = integer("Ergebnis")
val fight = optReference("Fight", FightTable).index() val fight = optReference("Fight", FightTable)
} }
class EventFight(id: EntityID<Int>) : IntEntity(id), Comparable<EventFight> { class EventFight(id: EntityID<Int>) : IntEntity(id), Comparable<EventFight> {
@@ -34,10 +34,6 @@ object EventGroupTable : IntIdTable("EventGroup", "Id") {
val pointsPerWin = integer("PointsPerWin").default(3) val pointsPerWin = integer("PointsPerWin").default(3)
val pointsPerLoss = integer("PointsPerLoss").default(0) val pointsPerLoss = integer("PointsPerLoss").default(0)
val pointsPerDraw = integer("PointsPerDraw").default(1) val pointsPerDraw = integer("PointsPerDraw").default(1)
init {
uniqueIndex(event, name)
}
} }
class EventGroup(id: EntityID<Int>) : IntEntity(id) { class EventGroup(id: EntityID<Int>) : IntEntity(id) {
@@ -94,7 +90,7 @@ class EventGroup(id: EntityID<Int>) : IntEntity(id) {
set(value) { set(value) {
groupPointsPerDraw = value groupPointsPerDraw = value
} }
val dependents by lazy { EventRelation.getGroupRelations(this) } val dependents by lazy { EventRelation.getGroupRelations(this).toList() }
val lastFight by lazy { Optional.ofNullable(fights.maxByOrNull { it.startTime }) } val lastFight by lazy { Optional.ofNullable(fights.maxByOrNull { it.startTime }) }
fun getId() = id.value fun getId() = id.value
@@ -29,7 +29,7 @@ import org.jetbrains.exposed.v1.dao.IntEntityClass
import org.jetbrains.exposed.v1.jdbc.select import org.jetbrains.exposed.v1.jdbc.select
object EventRelationTable : IntIdTable("EventRelation") { object EventRelationTable : IntIdTable("EventRelation") {
val fightId = reference("FightId", EventFightTable).index() val fightId = reference("FightId", EventFightTable)
val fightTeam = enumeration("FightTeam", EventRelation.FightTeam::class) val fightTeam = enumeration("FightTeam", EventRelation.FightTeam::class)
val fromType = enumeration("FromType", EventRelation.FromType::class) val fromType = enumeration("FromType", EventRelation.FromType::class)
val fromId = integer("FromId") val fromId = integer("FromId")
@@ -51,11 +51,11 @@ class EventRelation(id: EntityID<Int>) : IntEntity(id) {
@JvmStatic @JvmStatic
fun getFightRelations(fight: EventFight) = fun getFightRelations(fight: EventFight) =
useDb { find { (EventRelationTable.fromId eq fight.id.value) and (EventRelationTable.fromType eq FromType.FIGHT) }.toList() } useDb { find { (EventRelationTable.fromId eq fight.id.value) and (EventRelationTable.fromType eq FromType.FIGHT) } }
@JvmStatic @JvmStatic
fun getGroupRelations(group: EventGroup) = fun getGroupRelations(group: EventGroup) =
useDb { find { (EventRelationTable.fromId eq group.id.value) and (EventRelationTable.fromType eq FromType.GROUP) }.toList() } useDb { find { (EventRelationTable.fromId eq group.id.value) and (EventRelationTable.fromType eq FromType.GROUP) } }
@JvmStatic @JvmStatic
fun create(fight: EventFight, fightTeam: FightTeam, fromType: FromType, fromId: Int, fromPlace: Int) = useDb { fun create(fight: EventFight, fightTeam: FightTeam, fromType: FromType, fromId: Int, fromPlace: Int) = useDb {
+5 -5
View File
@@ -34,14 +34,14 @@ import org.jetbrains.exposed.v1.jdbc.update
import java.sql.Timestamp import java.sql.Timestamp
object FightTable : IntIdTable("Fight", "FightId") { object FightTable : IntIdTable("Fight", "FightId") {
val gamemode = varchar("Gamemode", 30).index() val gamemode = varchar("Gamemode", 30)
val server = text("Server") val server = text("Server")
val startTime = timestamp("StartTime") val startTime = timestamp("StartTime")
val duration = integer("Duration") val duration = integer("Duration")
val blueLeader = reference("BlueLeader", SteamwarUserTable).index() val blueLeader = reference("BlueLeader", SteamwarUserTable)
val redLeader = reference("RedLeader", SteamwarUserTable).index() val redLeader = reference("RedLeader", SteamwarUserTable)
val blueSchem = optReference("BlueSchem", SchematicNodeTable, onDelete = ReferenceOption.SET_NULL).index() val blueSchem = optReference("BlueSchem", SchematicNodeTable, onDelete = ReferenceOption.SET_NULL)
val redSchem = optReference("RedSchem", SchematicNodeTable, onDelete = ReferenceOption.SET_NULL).index() val redSchem = optReference("RedSchem", SchematicNodeTable, onDelete = ReferenceOption.SET_NULL)
val win = enumeration("Win", Fight.WinningTeam::class) val win = enumeration("Win", Fight.WinningTeam::class)
val winCondition = varchar("WinCondition", 100) val winCondition = varchar("WinCondition", 100)
val replayAvailable = bool("ReplayAvailable") val replayAvailable = bool("ReplayAvailable")
@@ -30,7 +30,7 @@ import org.jetbrains.exposed.v1.jdbc.insertIgnore
object FightPlayerTable : CompositeIdTable("FightPlayer") { object FightPlayerTable : CompositeIdTable("FightPlayer") {
val fightId = reference("FightId", FightTable) val fightId = reference("FightId", FightTable)
val userId = reference("UserId", SteamwarUserTable).index() val userId = reference("UserId", SteamwarUserTable)
val team = integer("Team") val team = integer("Team")
val kit = varchar("Kit", 64) val kit = varchar("Kit", 64)
val kills = integer("Kills") val kills = integer("Kills")
@@ -52,10 +52,6 @@ public final class GameModeConfig<M, W> {
private static final Map<String, GameModeConfig<?, String>> byGameName; private static final Map<String, GameModeConfig<?, String>> byGameName;
private static final Map<SchematicType, GameModeConfig<?, String>> bySchematicType; private static final Map<SchematicType, GameModeConfig<?, String>> bySchematicType;
public static <M> Collection<GameModeConfig<M, String>> getAll() {
return (Collection) byFileName.values();
}
public static <M> GameModeConfig<M, String> getByFileName(File file) { public static <M> GameModeConfig<M, String> getByFileName(File file) {
return (GameModeConfig<M, String>) byFileName.get(file.getName()); return (GameModeConfig<M, String>) byFileName.get(file.getName());
} }
@@ -91,24 +87,8 @@ public final class GameModeConfig<M, W> {
byFileName = new HashMap<>(); byFileName = new HashMap<>();
byGameName = new HashMap<>(); byGameName = new HashMap<>();
bySchematicType = new HashMap<>(); bySchematicType = new HashMap<>();
SchematicType.values();
DEFAULTS = SQLWrapper.impl.loadGameModeConfig(null); DEFAULTS = SQLWrapper.impl.loadGameModeConfig(null);
init();
}
public static void init() {
byFileName.clear();
byGameName.clear();
bySchematicType.clear();
File folder = SQLWrapper.impl.getSchemTypesFolder();
if (!folder.exists()) return;
if (!folder.isDirectory()) return;
for (File file : Objects.requireNonNull(folder.listFiles())) {
if (!file.getName().endsWith(".yml")) continue;
if (file.getName().endsWith(".kits.yml")) continue;
SQLWrapper.impl.loadGameModeConfig(file);
}
byFileName.values().forEach(gameModeConfig -> { byFileName.values().forEach(gameModeConfig -> {
List<SchematicType> subTypes = Collections.unmodifiableList(gameModeConfig.Schematic.SubTypesStrings.stream() List<SchematicType> subTypes = Collections.unmodifiableList(gameModeConfig.Schematic.SubTypesStrings.stream()
@@ -691,9 +671,9 @@ public final class GameModeConfig<M, W> {
loaded = loader.canLoad(); loaded = loader.canLoad();
Size = new SizeConfig(loader.with("Size")); Size = new SizeConfig(loader.with("Size"));
Inset = new InsetConfig(loader.with("Inset")); Inset = new InsetConfig(loader.with("Inset"));
Type = null; Type = loader.getSchematicType("Type", "Normal");
SubTypesStrings = loader.getStringList("SubTypes"); SubTypesStrings = loader.getStringList("SubTypes");
SubTypes = new ArrayList<>(); SubTypes = loader.getSchematicTypeList("SubTypes");
Shortcut = loader.getString("Shortcut", ""); Shortcut = loader.getString("Shortcut", "");
Material = loader.getMaterial("Material", "STONE_BUTTON"); Material = loader.getMaterial("Material", "STONE_BUTTON");
ManualCheck = loader.getBoolean("ManualCheck", true); ManualCheck = loader.getBoolean("ManualCheck", true);
@@ -30,8 +30,8 @@ import org.jetbrains.exposed.v1.dao.CompositeEntityClass
import java.util.* import java.util.*
object IgnoreSystemTable: CompositeIdTable("IgnoredPlayers") { object IgnoreSystemTable: CompositeIdTable("IgnoredPlayers") {
val ignorer = reference("Ignorer", SteamwarUserTable).index() val ignorer = reference("Ignorer", SteamwarUserTable)
val ignored = reference("Ignored", SteamwarUserTable).index() val ignored = reference("Ignored", SteamwarUserTable)
override val primaryKey = PrimaryKey(ignorer, ignored) override val primaryKey = PrimaryKey(ignorer, ignored)
@@ -35,7 +35,7 @@ import java.io.InputStream
import java.util.zip.GZIPInputStream import java.util.zip.GZIPInputStream
object NodeDataTable: CompositeIdTable("NodeData") { object NodeDataTable: CompositeIdTable("NodeData") {
val nodeId = reference("NodeId", SchematicNodeTable).index() val nodeId = reference("NodeId", SchematicNodeTable)
val createdAt = timestamp("CreatedAt").defaultExpression(CurrentTimestamp).entityId() val createdAt = timestamp("CreatedAt").defaultExpression(CurrentTimestamp).entityId()
val nodeFormat = enumeration("NodeFormat", NodeData.SchematicFormat::class) val nodeFormat = enumeration("NodeFormat", NodeData.SchematicFormat::class)
val schemData = blob("SchemData") val schemData = blob("SchemData")
@@ -20,7 +20,6 @@
package de.steamwar.sql package de.steamwar.sql
import de.steamwar.sql.internal.useDb import de.steamwar.sql.internal.useDb
import org.jetbrains.exposed.v1.core.ReferenceOption
import org.jetbrains.exposed.v1.core.dao.id.EntityID import org.jetbrains.exposed.v1.core.dao.id.EntityID
import org.jetbrains.exposed.v1.core.dao.id.IdTable import org.jetbrains.exposed.v1.core.dao.id.IdTable
import org.jetbrains.exposed.v1.core.eq import org.jetbrains.exposed.v1.core.eq
@@ -33,8 +32,8 @@ import java.sql.Timestamp
import java.time.Instant import java.time.Instant
object NodeDownloadTable: IdTable<Int>("NodeDownload") { object NodeDownloadTable: IdTable<Int>("NodeDownload") {
override val id = reference("NodeId", SchematicNodeTable, onDelete = ReferenceOption.CASCADE).uniqueIndex() override val id = reference("NodeId", SchematicNodeTable).uniqueIndex()
val link = varchar("Link", 255).uniqueIndex() val link = varchar("Link", 255)
val timestamp = timestamp("Timestamp").defaultExpression(CurrentTimestamp) val timestamp = timestamp("Timestamp").defaultExpression(CurrentTimestamp)
} }
@@ -20,7 +20,6 @@
package de.steamwar.sql package de.steamwar.sql
import de.steamwar.sql.internal.useDb import de.steamwar.sql.internal.useDb
import org.jetbrains.exposed.v1.core.ReferenceOption
import org.jetbrains.exposed.v1.core.and import org.jetbrains.exposed.v1.core.and
import org.jetbrains.exposed.v1.core.dao.id.CompositeID import org.jetbrains.exposed.v1.core.dao.id.CompositeID
import org.jetbrains.exposed.v1.core.dao.id.CompositeIdTable import org.jetbrains.exposed.v1.core.dao.id.CompositeIdTable
@@ -33,9 +32,9 @@ import java.util.*
import kotlin.jvm.optionals.getOrNull import kotlin.jvm.optionals.getOrNull
object NodeMemberTable : CompositeIdTable("NodeMember") { object NodeMemberTable : CompositeIdTable("NodeMember") {
val node = reference("NodeId", SchematicNodeTable, onDelete = ReferenceOption.CASCADE, onUpdate = ReferenceOption.CASCADE).index() val node = reference("NodeId", SchematicNodeTable)
val userId = reference("UserId", SteamwarUserTable).index() val userId = reference("UserId", SteamwarUserTable)
val parentNode = optReference("ParentId", SchematicNodeTable).index() val parentNode = optReference("ParentId", SchematicNodeTable)
override val primaryKey = PrimaryKey(node, userId) override val primaryKey = PrimaryKey(node, userId)
@@ -95,7 +94,7 @@ class NodeMember(id: EntityID<CompositeID>) : CompositeEntity(id) {
{ Optional.ofNullable(it?.value) }) { Optional.ofNullable(it?.value) })
private set private set
fun setParentId(id: Int?) = useDb { fun setParentId(id: Int?) {
parent = Optional.ofNullable(id) parent = Optional.ofNullable(id)
} }
@@ -41,7 +41,6 @@ object PersonalKitTable: CompositeIdTable("PersonalKit") {
init { init {
addIdColumn(userId) addIdColumn(userId)
index(false, userId, gamemode)
} }
} }
@@ -34,16 +34,12 @@ import java.util.function.Consumer
object PunishmentTable : IntIdTable("Punishments", "PunishmentId") { object PunishmentTable : IntIdTable("Punishments", "PunishmentId") {
val userId = reference("UserId", SteamwarUserTable) val userId = reference("UserId", SteamwarUserTable)
val punisher = reference("Punisher", SteamwarUserTable).index() val punisher = reference("Punisher", SteamwarUserTable)
val type = enumerationByName("Type", 32, Punishment.PunishmentType::class) val type = enumerationByName("Type", 32, Punishment.PunishmentType::class)
val startTime = timestamp("StartTime") val startTime = timestamp("StartTime")
val endTime = timestamp("EndTime") val endTime = timestamp("EndTime")
val perma = bool("Perma") val perma = bool("Perma")
val reason = text("Reason") val reason = text("Reason")
init {
index(false, userId, type)
}
} }
class Punishment(id: EntityID<Int>) : IntEntity(id) { class Punishment(id: EntityID<Int>) : IntEntity(id) {
@@ -30,7 +30,7 @@ import org.jetbrains.exposed.v1.dao.CompositeEntityClass
object RefereeTable: CompositeIdTable("Referee") { object RefereeTable: CompositeIdTable("Referee") {
val eventId = reference("EventId", EventTable) val eventId = reference("EventId", EventTable)
val userId = reference("UserId", SteamwarUserTable).index() val userId = reference("UserId", SteamwarUserTable)
override val primaryKey = PrimaryKey(eventId, userId) override val primaryKey = PrimaryKey(eventId, userId)
@@ -36,5 +36,8 @@ public interface SQLWrapper<M> {
return Collections.emptyList(); return Collections.emptyList();
} }
default void processSchematicType(GameModeConfig<?, String> gameModeConfig) {
}
void additionalExceptionMetadata(StringBuilder builder); void additionalExceptionMetadata(StringBuilder builder);
} }
@@ -34,17 +34,13 @@ import java.util.*
import java.util.function.Consumer import java.util.function.Consumer
object SchematicNodeTable : IntIdTable("SchematicNode", "NodeId") { object SchematicNodeTable : IntIdTable("SchematicNode", "NodeId") {
val owner = reference("NodeOwner", SteamwarUserTable).index() val owner = reference("NodeOwner", SteamwarUserTable)
val name = varchar("NodeName", 64) val name = varchar("NodeName", 64)
val parent = optReference("ParentNode", SchematicNodeTable).index() val parent = optReference("ParentNode", SchematicNodeTable)
val lastUpdate = timestamp("LastUpdate").defaultExpression(CurrentTimestamp) val lastUpdate = timestamp("LastUpdate").defaultExpression(CurrentTimestamp)
val item = text("NodeItem") val item = text("NodeItem")
val type = varchar("NodeType", 16).nullable().index() val type = varchar("NodeType", 16).nullable()
val config = integer("Config") val config = integer("Config")
init {
uniqueIndex(parent, owner, name)
}
} }
class SchematicNode(id: EntityID<Int>) : IntEntity(id) { class SchematicNode(id: EntityID<Int>) : IntEntity(id) {
@@ -19,7 +19,11 @@
package de.steamwar.sql package de.steamwar.sql
import java.io.File
import java.util.* import java.util.*
import java.util.Locale
import java.util.Locale.getDefault
import java.util.stream.Collectors
data class SchematicType( data class SchematicType(
val name: String, val name: String,
@@ -43,65 +47,58 @@ data class SchematicType(
@JvmField @JvmField
val Normal = SchematicType("Normal", "", Type.NORMAL, null, "STONE_BUTTON", false) val Normal = SchematicType("Normal", "", Type.NORMAL, null, "STONE_BUTTON", false)
private val types: MutableList<SchematicType> = mutableListOf() private val types: List<SchematicType>
private val fromDB: MutableMap<String, SchematicType> = mutableMapOf() private val fromDB: Map<String, SchematicType>?
init { init {
GameModeConfig.init() val tmpTypes = mutableListOf<SchematicType>()
init() val tmpFromDB = mutableMapOf<String, SchematicType>()
}
@JvmStatic tmpTypes.add(Normal)
fun init() { tmpFromDB[Normal.toDB()] = Normal
types.clear()
fromDB.clear()
types.add(Normal) val folder = SQLWrapper.impl.schemTypesFolder
fromDB[Normal.toDB()] = Normal if (folder.exists()) {
for (configFile in Arrays.stream<File?>(folder.listFiles { _, name ->
for (gameModeConfig in GameModeConfig.getAll<Any>()) { name.endsWith(
val type = gameModeConfig.Schematic.Type ".yml"
?: continue ) && !name.endsWith(".kits.yml")
if (fromDB.containsKey(type.toDB())) continue }).sorted().collect(Collectors.toList())) {
val gameModeConfig = SQLWrapper.impl.loadGameModeConfig(configFile)
types.add(type) if (gameModeConfig.Schematic.Type == null) continue
fromDB[type.toDB()] = type if (tmpFromDB.containsKey(gameModeConfig.Schematic.Type.toDB())) continue
if (gameModeConfig.CheckQuestions.isNotEmpty() && type.checkType != null) { val current = gameModeConfig.Schematic.Type
types.add(type.checkType) if (gameModeConfig.CheckQuestions.isNotEmpty()) {
fromDB[type.checkType.toDB()] = type.checkType val checkType = current.checkType
tmpTypes.add(checkType!!)
tmpFromDB[checkType.toDB()] = checkType
}
tmpTypes.add(current)
tmpFromDB[current.toDB()] = current
SQLWrapper.impl.processSchematicType(gameModeConfig)
} }
} }
types = tmpTypes.toList()
fromDB = tmpFromDB.toMap()
} }
@JvmStatic @JvmStatic
fun values() = fun values() = types
types
@JvmStatic @JvmStatic
fun fromDB(value: String) = fun fromDB(value: String) = fromDB?.let { it[value.lowercase()] }
fromDB[value.lowercase()]
} }
fun name() = fun name() = name
name fun toDB() = name.lowercase()
fun toDB() = fun check() = type == Type.CHECK_TYPE
name.lowercase() fun fightType() = type == Type.FIGHT_TYPE
fun writeable() = type == Type.NORMAL
fun check() = fun checkType() = if (manualCheck) checkType else this
type == Type.CHECK_TYPE fun isAssignable() = type == Type.NORMAL || (type == Type.FIGHT_TYPE && checkType != null) || !manualCheck
fun fightType() =
type == Type.FIGHT_TYPE
fun writeable() =
type == Type.NORMAL
fun checkType() =
if (manualCheck) checkType else this
fun isAssignable() =
type == Type.NORMAL || (type == Type.FIGHT_TYPE && checkType != null) || !manualCheck
enum class Type { enum class Type {
NORMAL, NORMAL,
+1 -5
View File
@@ -28,13 +28,9 @@ import org.jetbrains.exposed.v1.dao.IntEntity
import org.jetbrains.exposed.v1.dao.IntEntityClass import org.jetbrains.exposed.v1.dao.IntEntityClass
object ScriptTable: IntIdTable("Script") { object ScriptTable: IntIdTable("Script") {
val userId = reference("UserId", SteamwarUserTable).index() val userId = reference("UserId", SteamwarUserTable)
val name = varchar("Name", 64) val name = varchar("Name", 64)
val code = text("Code") val code = text("Code")
init {
uniqueIndex(userId, name)
}
} }
class Script(id: EntityID<Int>) : IntEntity(id) { class Script(id: EntityID<Int>) : IntEntity(id) {
@@ -28,7 +28,7 @@ import org.jetbrains.exposed.v1.jdbc.insert
import java.sql.Timestamp import java.sql.Timestamp
object SessionTable: Table("Session") { object SessionTable: Table("Session") {
val userId = reference("UserId", SteamwarUserTable).index() val userId = reference("UserId", SteamwarUserTable)
val startTime = timestamp("StartTime") val startTime = timestamp("StartTime")
val endTime = timestamp("EndTime").defaultExpression(CurrentTimestamp) val endTime = timestamp("EndTime").defaultExpression(CurrentTimestamp)
} }
@@ -37,15 +37,15 @@ import javax.crypto.SecretKeyFactory
import javax.crypto.spec.PBEKeySpec import javax.crypto.spec.PBEKeySpec
object SteamwarUserTable : IntIdTable("UserData", "id") { object SteamwarUserTable : IntIdTable("UserData", "id") {
val uuid = varchar("UUID", 36).uniqueIndex() val uuid = varchar("UUID", 36)
val username = varchar("UserName", 32).index() val username = varchar("UserName", 32)
val team = reference("Team", TeamTable).index() val team = reference("Team", TeamTable)
val leader = bool("Leader") val leader = bool("Leader")
val locale = varchar("Locale", 16).nullable() val locale = varchar("Locale", 16).nullable()
val manualLocale = bool("ManualLocale") val manualLocale = bool("ManualLocale")
val bedrock = bool("Bedrock") val bedrock = bool("Bedrock")
val password = text("Password").nullable() val password = text("Password").nullable()
val discordId = long("DiscordId").nullable().uniqueIndex() val discordId = long("DiscordId").nullable()
} }
class SteamwarUser(id: EntityID<Int>): IntEntity(id) { class SteamwarUser(id: EntityID<Int>): IntEntity(id) {
+2 -2
View File
@@ -28,9 +28,9 @@ import org.jetbrains.exposed.v1.dao.IntEntityClass
import org.jetbrains.exposed.v1.jdbc.select import org.jetbrains.exposed.v1.jdbc.select
object TeamTable : IntIdTable("Team", "TeamID") { object TeamTable : IntIdTable("Team", "TeamID") {
val kuerzel = varchar("TeamKuerzel", 10).index() val kuerzel = varchar("TeamKuerzel", 10)
val color = char("TeamColor", 1).default("8") val color = char("TeamColor", 1).default("8")
val name = varchar("TeamName", 16).index() val name = varchar("TeamName", 16)
val deleted = bool("TeamDeleted").default(false) val deleted = bool("TeamDeleted").default(false)
} }
@@ -32,8 +32,8 @@ import org.jetbrains.exposed.v1.jdbc.deleteWhere
import org.jetbrains.exposed.v1.jdbc.insertIgnore import org.jetbrains.exposed.v1.jdbc.insertIgnore
object TeamTeilnahmeTable : CompositeIdTable("TeamTeilnahme") { object TeamTeilnahmeTable : CompositeIdTable("TeamTeilnahme") {
val teamId = reference("teamId", TeamTable).index() val teamId = reference("teamId", TeamTable)
val eventId = reference("eventId", EventTable).index() val eventId = reference("eventId", EventTable)
val placement = integer("Placement").nullable() val placement = integer("Placement").nullable()
override val primaryKey = PrimaryKey(teamId, eventId) override val primaryKey = PrimaryKey(teamId, eventId)
+3 -3
View File
@@ -33,10 +33,10 @@ import java.sql.Timestamp
import java.util.* import java.util.*
object TokenTable: IntIdTable("Token") { object TokenTable: IntIdTable("Token") {
val name = varchar("Name", 64).uniqueIndex() val name = varchar("Name", 64)
val owner = reference("Owner", SteamwarUserTable).index() val owner = reference("Owner", SteamwarUserTable)
val created = timestamp("Created").defaultExpression(CurrentTimestamp) val created = timestamp("Created").defaultExpression(CurrentTimestamp)
val hash = varchar("Hash", 88).uniqueIndex() val hash = varchar("Hash", 88)
} }
class Token(id: EntityID<Int>): IntEntity(id) { class Token(id: EntityID<Int>): IntEntity(id) {
@@ -28,7 +28,7 @@ import org.jetbrains.exposed.v1.jdbc.insert
import org.jetbrains.exposed.v1.jdbc.selectAll import org.jetbrains.exposed.v1.jdbc.selectAll
object UserPermTable: Table("UserPerm") { object UserPermTable: Table("UserPerm") {
val user = reference("User", SteamwarUserTable.id).index() val user = reference("User", SteamwarUserTable.id)
val perm = enumerationByName("Perm", 32, UserPerm::class) val perm = enumerationByName("Perm", 32, UserPerm::class)
override val primaryKey = PrimaryKey(user, perm) override val primaryKey = PrimaryKey(user, perm)
@@ -67,7 +67,7 @@ object KotlinDatabase {
} }
} }
fun <T> useDb(statement: JdbcTransaction.() -> T): T { fun <T: Any?> useDb(statement: JdbcTransaction.() -> T): T {
KotlinDatabase.ensureConnected() KotlinDatabase.ensureConnected()
return TransactionManager.currentOrNull()?.statement() ?: transaction(KotlinDatabase.db) { return TransactionManager.currentOrNull()?.statement() ?: transaction(KotlinDatabase.db) {
statement() statement()
@@ -42,5 +42,4 @@ dependencies {
compileOnly(libs.fastutil) compileOnly(libs.fastutil)
compileOnly(libs.authlib) compileOnly(libs.authlib)
compileOnly(project(":FightSystem:FightSystem_14"))
} }
@@ -1,51 +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.fightsystem.listener;
import de.steamwar.fightsystem.Config;
import de.steamwar.fightsystem.states.FightState;
import de.steamwar.fightsystem.states.StateDependentTask;
import net.minecraft.world.entity.projectile.windcharge.WindCharge;
import org.bukkit.Location;
public class WindchargeStopper21 implements WindchargeStopper.IWindchargeStopper {
public WindchargeStopper21() {
new StateDependentTask(true, FightState.Running, this::run, 1, 1);
}
private static final int middleLine = Config.SpecSpawn.getBlockZ();
private static final Class<?> windChargeClass = WindCharge.class;
private void run() {
Recording.iterateOverEntities(windChargeClass::isInstance, entity -> {
Location location = entity.getLocation();
Location prevLocation = location.clone().subtract(entity.getVelocity());
boolean passedMiddle = location.getBlockZ() > middleLine && prevLocation.getBlockZ() > middleLine ||
location.getBlockZ() < middleLine && prevLocation.getBlockZ() < middleLine;
if(!passedMiddle) {
entity.remove();
}
});
}
}
@@ -1,29 +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.fightsystem.utils;
import org.bukkit.inventory.ItemStack;
public class FlatteningWrapper21 extends FlatteningWrapper14 {
@Override
public boolean hasAttributeModifier(ItemStack stack) {
return stack.hasItemMeta() && stack.getItemMeta() != null && stack.getItemMeta().hasAttributeModifiers();
}
}
@@ -38,27 +38,6 @@ public class ReflectionWrapper21 implements ReflectionWrapper {
FORBIDDEN_TYPES.add(DataComponentTypes.BLOCKS_ATTACKS); FORBIDDEN_TYPES.add(DataComponentTypes.BLOCKS_ATTACKS);
FORBIDDEN_TYPES.add(DataComponentTypes.BUNDLE_CONTENTS); FORBIDDEN_TYPES.add(DataComponentTypes.BUNDLE_CONTENTS);
FORBIDDEN_TYPES.add(DataComponentTypes.CUSTOM_MODEL_DATA); FORBIDDEN_TYPES.add(DataComponentTypes.CUSTOM_MODEL_DATA);
FORBIDDEN_TYPES.add(DataComponentTypes.ATTRIBUTE_MODIFIERS);
FORBIDDEN_TYPES.add(DataComponentTypes.TOOL);
FORBIDDEN_TYPES.add(DataComponentTypes.WEAPON);
FORBIDDEN_TYPES.add(DataComponentTypes.FOOD);
FORBIDDEN_TYPES.add(DataComponentTypes.CONSUMABLE);
FORBIDDEN_TYPES.add(DataComponentTypes.POTION_CONTENTS);
FORBIDDEN_TYPES.add(DataComponentTypes.STORED_ENCHANTMENTS);
FORBIDDEN_TYPES.add(DataComponentTypes.CAN_BREAK);
FORBIDDEN_TYPES.add(DataComponentTypes.CAN_PLACE_ON);
FORBIDDEN_TYPES.add(DataComponentTypes.MAX_DAMAGE);
FORBIDDEN_TYPES.add(DataComponentTypes.USE_REMAINDER);
FORBIDDEN_TYPES.add(DataComponentTypes.USE_COOLDOWN);
FORBIDDEN_TYPES.add(DataComponentTypes.SUSPICIOUS_STEW_EFFECTS);
FORBIDDEN_TYPES.add(DataComponentTypes.CHARGED_PROJECTILES);
FORBIDDEN_TYPES.add(DataComponentTypes.INTANGIBLE_PROJECTILE);
FORBIDDEN_TYPES.add(DataComponentTypes.FIREWORKS);
FORBIDDEN_TYPES.add(DataComponentTypes.FIREWORK_EXPLOSION);
FORBIDDEN_TYPES.add(DataComponentTypes.EQUIPPABLE);
FORBIDDEN_TYPES.add(DataComponentTypes.REPAIR_COST);
FORBIDDEN_TYPES.add(DataComponentTypes.ENCHANTABLE);
} }
@Override @Override
@@ -1,23 +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.fightsystem.listener;
public class WindchargeStopper8 implements WindchargeStopper.IWindchargeStopper {
}
@@ -98,12 +98,10 @@ public class FightSystem extends JavaPlugin {
new StateDependentListener(ArenaMode.All, FightState.All, BountifulWrapper.impl.newDenyArrowPickupListener()); new StateDependentListener(ArenaMode.All, FightState.All, BountifulWrapper.impl.newDenyArrowPickupListener());
new OneShotStateDependent(ArenaMode.All, FightState.PreSchemSetup, () -> Fight.playSound(SWSound.BLOCK_NOTE_PLING.getSound(), 100.0f, 2.0f)); new OneShotStateDependent(ArenaMode.All, FightState.PreSchemSetup, () -> Fight.playSound(SWSound.BLOCK_NOTE_PLING.getSound(), 100.0f, 2.0f));
new OneShotStateDependent(ArenaMode.Test, FightState.All, WorldEditRendererCUIEditor::new); new OneShotStateDependent(ArenaMode.Test, FightState.All, WorldEditRendererCUIEditor::new);
if (Core.getVersion() >= 19) { try {
try { Bukkit.getWorlds().get(0).setGameRule(GameRule.REDUCED_DEBUG_INFO, ArenaMode.AntiTest.contains(Config.mode));
Bukkit.getWorlds().get(0).setGameRule(GameRule.REDUCED_DEBUG_INFO, ArenaMode.AntiTest.contains(Config.mode)); } catch (Exception e) {
} catch (Exception e) { // Ignore if failed!
// Ignore if failed!
}
} }
techHider = new TechHiderWrapper(); techHider = new TechHiderWrapper();
@@ -23,14 +23,12 @@ import com.sk89q.worldedit.extent.clipboard.Clipboard;
import de.steamwar.fightsystem.ArenaMode; 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.FightTeam; import de.steamwar.fightsystem.fight.FightTeam;
import de.steamwar.fightsystem.fight.JoinRequest; import de.steamwar.fightsystem.fight.JoinRequest;
import de.steamwar.fightsystem.listener.Chat; import de.steamwar.fightsystem.listener.Chat;
import de.steamwar.fightsystem.record.GlobalRecorder; import de.steamwar.fightsystem.record.GlobalRecorder;
import de.steamwar.fightsystem.states.FightState; import de.steamwar.fightsystem.states.FightState;
import de.steamwar.fightsystem.states.OneShotStateDependent; import de.steamwar.fightsystem.states.OneShotStateDependent;
import de.steamwar.fightsystem.utils.Region;
import de.steamwar.sql.SchematicNode; import de.steamwar.sql.SchematicNode;
import de.steamwar.sql.SteamwarUser; import de.steamwar.sql.SteamwarUser;
import lombok.Getter; import lombok.Getter;
@@ -54,7 +52,6 @@ import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Villager; import org.bukkit.entity.Villager;
import org.bukkit.event.player.PlayerTeleportEvent; import org.bukkit.event.player.PlayerTeleportEvent;
import org.bukkit.scheduler.BukkitTask; import org.bukkit.scheduler.BukkitTask;
import org.bukkit.util.Vector;
import java.util.*; import java.util.*;
import java.util.logging.Level; import java.util.logging.Level;
@@ -87,6 +84,8 @@ public abstract class AI {
private final BukkitTask task; private final BukkitTask task;
private final Queue<Action> queue = new ArrayDeque<>(); private final Queue<Action> queue = new ArrayDeque<>();
protected final NavMesh navMesh;
protected AI(FightTeam team, SteamwarUser user) { protected AI(FightTeam team, SteamwarUser user) {
this.team = team; this.team = team;
@@ -98,6 +97,8 @@ public abstract class AI {
ais.put(entity.getUniqueId(), this); ais.put(entity.getUniqueId(), this);
team.addMember(entity, user); team.addMember(entity, user);
navMesh = new NavMesh(team);
if(FightState.Schem.contains(FightState.getFightState())) if(FightState.Schem.contains(FightState.getFightState()))
Bukkit.getScheduler().runTask(FightSystem.getPlugin(), () -> schematic(team.getClipboard())); Bukkit.getScheduler().runTask(FightSystem.getPlugin(), () -> schematic(team.getClipboard()));
} }
@@ -134,58 +135,61 @@ public abstract class AI {
Chat.broadcastChat("PARTICIPANT_CHAT", team.getColoredName(), entity.getName(), message); Chat.broadcastChat("PARTICIPANT_CHAT", team.getColoredName(), entity.getName(), message);
} }
public Vector getPosition() { public LocalCoordinate getPosition() {
Location location = entity.getLocation(); return WorldCoordinate.from(entity.getLocation()).toLocal(team);
Region extend = team.getExtendRegion();
if(Fight.getUnrotated() == team)
return new Vector(
location.getX() - extend.getMinX(),
location.getY() - team.getSchemRegion().getMinY(),
location.getZ() - extend.getMinZ()
);
else
return new Vector(
extend.getMaxX() - location.getX(),
location.getY() - team.getSchemRegion().getMinY(),
extend.getMaxZ() - location.getZ()
);
} }
public Material getBlock(Vector pos) { public Material getBlock(WorldCoordinate pos) {
return getBlock(pos.toLocal(team));
}
public Material getBlock(LocalCoordinate pos) {
queue.add(new Action(1)); queue.add(new Action(1));
return translate(pos).getBlock().getType(); return pos.toWorld(team).toLocation(Config.world).getBlock().getType();
} }
public BlockData getBlockData(Vector pos) { public BlockData getBlockData(WorldCoordinate pos) {
return getBlockData(pos.toLocal(team));
}
public BlockData getBlockData(LocalCoordinate pos) {
queue.add(new Action(1)); queue.add(new Action(1));
return translate(pos).getBlock().getBlockData(); return pos.toWorld(team).toLocation(Config.world).getBlock().getBlockData();
} }
public void setTNT(Vector pos) { public void setTNT(WorldCoordinate pos) {
setTNT(pos.toLocal(team));
}
public void setTNT(LocalCoordinate pos) {
queue.add(new Action(1) { queue.add(new Action(1) {
@Override @Override
public void run() { public void run() {
if(FightState.getFightState() != FightState.RUNNING) if(FightState.getFightState() != FightState.RUNNING)
return; return;
Location location = translate(pos); Location location = pos.toWorld(team).toLocation(Config.world);
if(interactionDistanceViolation(location)) { if(interactionDistanceViolation(location)) {
chat("InteractionDistanceViolation: setTNT"); chat("InteractionDistanceViolation: setTNT");
return; return;
} }
Block block = location.getBlock(); Block block = location.getBlock();
if(block.getType() == Material.AIR) if(block.getType() == Material.AIR || block.getType() == Material.WATER || block.getType() == Material.LAVA || block.getType() == Material.TNT)
block.setType(Material.TNT); block.setType(Material.TNT);
} }
}); });
} }
public void interact(Vector pos) { public void interact(WorldCoordinate pos) {
interact(pos.toLocal(team));
}
public void interact(LocalCoordinate pos) {
queue.add(new Action(1) { queue.add(new Action(1) {
@Override @Override
public void run() { public void run() {
Location location = translate(pos); Location location = pos.toWorld(team).toLocation(Config.world);
if(interactionDistanceViolation(location)) { if(interactionDistanceViolation(location)) {
chat("InteractionDistanceViolation: interact"); chat("InteractionDistanceViolation: interact");
return; return;
@@ -196,11 +200,15 @@ public abstract class AI {
}); });
} }
public void interact(Vector pos, int n) { public void interact(WorldCoordinate pos, int n) {
interact(pos.toLocal(team), n);
}
public void interact(LocalCoordinate pos, int n) {
queue.add(new Action(1) { queue.add(new Action(1) {
@Override @Override
public void run() { public void run() {
Location location = translate(pos); Location location = pos.toWorld(team).toLocation(Config.world);
if (interactionDistanceViolation(location)) if (interactionDistanceViolation(location))
return; return;
Block block = location.getBlock(); Block block = location.getBlock();
@@ -217,39 +225,75 @@ public abstract class AI {
}); });
} }
public void move(Vector pos) { public MoveResult checkMove(WorldCoordinate target) {
Location location = entity.getLocation();
if(!entity.isOnGround() && location.getBlock().getType() != Material.LADDER) {
return MoveResult.FALLING;
}
if(Math.abs(location.getX() - target.getX()) > 1.5 || Math.abs(location.getY() - target.getY()) > 1.5 || Math.abs(location.getZ() - target.getZ()) > 1.5) {
return MoveResult.OVERDISTANCE;
}
if(!team.getFightPlayer(entity).canEntern() && !team.getExtendRegion().inRegion(target.toLocation(null)))
return MoveResult.OUT_OF_BORDER;
return MoveResult.OK;
}
public enum MoveResult {
FALLING,
OVERDISTANCE,
OUT_OF_BORDER,
OK,
}
public void move(WorldCoordinate pos) {
queue.add(new Action(MOVEMENT_DELAY) { queue.add(new Action(MOVEMENT_DELAY) {
@Override @Override
public void run() { public void run() {
Location location = entity.getLocation(); MoveResult moveResult = checkMove(pos);
if(!entity.isOnGround() && location.getBlock().getType() != Material.LADDER) { switch (moveResult) {
FightSystem.getPlugin().getLogger().log(Level.INFO, "Entity falling"); case FALLING:
return; FightSystem.getPlugin().getLogger().log(Level.INFO, "Entity falling");
return;
case OVERDISTANCE:
FightSystem.getPlugin().getLogger().log(Level.INFO, () -> entity.getName() + ": Overdistance movement " + entity.getLocation().toVector() + " " + pos);
return;
case OUT_OF_BORDER:
return;
default:
break;
} }
Location target = translate(pos); Location currentLocation = entity.getLocation();
if(Math.abs(location.getX() - target.getX()) > 1.0 || Math.abs(location.getY() - target.getY()) > 1.5 || Math.abs(location.getZ() - target.getZ()) > 1.0) { Location target = pos.toLocation(Config.world);
FightSystem.getPlugin().getLogger().log(Level.INFO, () -> entity.getName() + ": Overdistance movement " + location.toVector() + " " + target.toVector()); if (currentLocation.getBlockX() != target.getBlockX() || currentLocation.getBlockY() != target.getBlockY() || currentLocation.getBlockZ() != target.getBlockZ()) {
return; target.setDirection(target.toVector().subtract(currentLocation.toVector()));
}
if (currentLocation.getBlockX() == target.getBlockX() && currentLocation.getBlockZ() == target.getBlockZ()) {
target.setYaw(currentLocation.getYaw());
} }
if(!team.getFightPlayer(entity).canEntern() && !team.getExtendRegion().inRegion(target))
return;
if(!entity.teleport(target, PlayerTeleportEvent.TeleportCause.PLUGIN)) if(!entity.teleport(target, PlayerTeleportEvent.TeleportCause.PLUGIN))
FightSystem.getPlugin().getLogger().log(Level.INFO, "Entity not teleported: " + entity.isValid()); FightSystem.getPlugin().getLogger().log(Level.INFO, "Entity not teleported: " + entity.isValid());
GlobalRecorder.getInstance().entityMoves(entity); GlobalRecorder.getInstance().entityMoves(entity);
navMesh.update(getPosition().toWorld(team));
} }
}); });
} }
public void move(LocalCoordinate pos) {
move(pos.toWorld(team));
}
private boolean interactionDistanceViolation(Location location) { private boolean interactionDistanceViolation(Location location) {
return location.distance(entity.getEyeLocation()) > INTERACTION_RANGE; return location.distance(entity.getEyeLocation()) > INTERACTION_RANGE;
} }
private void interact(Block block) { private void interact(Block block) {
BlockData data = block.getBlockData(); //TODO only 1.14+ compatible at the moment BlockData data = block.getBlockData(); //TODO only 1.14+ compatible at the moment
System.out.println(block + " " + data);
if (data instanceof NoteBlock) { if (data instanceof NoteBlock) {
NoteBlock noteBlock = (NoteBlock) data; NoteBlock noteBlock = (NoteBlock) data;
Note note = noteBlock.getNote(); Note note = noteBlock.getNote();
@@ -280,6 +324,7 @@ public abstract class AI {
} }
powerable.setPowered(!isPowered); powerable.setPowered(!isPowered);
System.out.println(powerable);
} }
block.setBlockData(data); block.setBlockData(data);
if(data instanceof Switch) { if(data instanceof Switch) {
@@ -319,24 +364,6 @@ public abstract class AI {
queue.poll().run(); queue.poll().run();
} }
public Location translate(Vector pos) {
Region extend = team.getExtendRegion();
if(Fight.getUnrotated() == team)
return new Location(
Config.world,
pos.getX() + extend.getMinX(),
pos.getY() + team.getSchemRegion().getMinY(),
pos.getZ() + extend.getMinZ()
);
else
return new Location(
Config.world,
extend.getMaxX() - pos.getX(),
pos.getY() + team.getSchemRegion().getMinY(),
extend.getMaxZ() - pos.getZ()
);
}
private static class Action { private static class Action {
private int delay; private int delay;
public Action(int delay) { public Action(int delay) {
@@ -20,8 +20,10 @@
package de.steamwar.fightsystem.ai; package de.steamwar.fightsystem.ai;
import de.steamwar.Reflection; import de.steamwar.Reflection;
import de.steamwar.core.Core;
import de.steamwar.fightsystem.ArenaMode; import de.steamwar.fightsystem.ArenaMode;
import de.steamwar.fightsystem.Config; import de.steamwar.fightsystem.Config;
import de.steamwar.fightsystem.ai.yoyonow.YoyoNowAI;
import de.steamwar.fightsystem.fight.FightTeam; import de.steamwar.fightsystem.fight.FightTeam;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Getter; import lombok.Getter;
@@ -35,7 +37,8 @@ import java.util.stream.Collectors;
@AllArgsConstructor @AllArgsConstructor
public class AIManager { public class AIManager {
private static final List<AIManager> AIs = Arrays.asList( private static final List<AIManager> AIs = Arrays.asList(
new AIManager(DummyAI.class, Material.STONE, () -> ArenaMode.Test.contains(Config.mode)) new AIManager(DummyAI.class, Material.STONE, () -> ArenaMode.Test.contains(Config.mode)),
new AIManager(YoyoNowAI.class, Material.SLIME_BLOCK, () -> Config.GameModeConfig.Schematic.Type.toDB().equals("miniwargear") && Core.getVersion() >= 14)
); );
public static List<AIManager> availableAIs() { public static List<AIManager> availableAIs() {
@@ -0,0 +1,180 @@
/*
* 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.fightsystem.ai;
import de.steamwar.fightsystem.ai.schematic.Cannon;
import java.util.ArrayList;
import java.util.List;
public interface Action {
Result step(AI ai);
enum Result {
ONGOING,
FINISHED,
FAILED,
;
}
class MultiAction implements Action {
protected final List<Action> actions = new ArrayList<>();
@Override
public Result step(AI ai) {
if (!actions.isEmpty()) {
Action.Result result = actions.get(0).step(ai);
if (result == Action.Result.FAILED) {
ai.chat(this + " + " + actions.get(0) + " failed");
actions.clear();
return Result.FAILED;
}
if (result == Action.Result.FINISHED) {
ai.chat(this + " + " + actions.get(0) + " finished");
actions.remove(0);
}
}
return actions.isEmpty() ? Result.FINISHED : Result.ONGOING;
}
}
class WaitAction implements Action {
private int delay;
public WaitAction(int delay) {
this.delay = delay;
}
@Override
public Result step(AI ai) {
if (--delay > 0) {
return Result.ONGOING;
} else {
return Result.FINISHED;
}
}
}
class MoveAction implements Action {
private final WorldCoordinate destination;
private List<WorldCoordinate> path;
private int overDistanceCounter = 0;
public MoveAction(AI ai, LocalCoordinate destination) {
this(ai, destination.toWorld(ai.team));
}
public MoveAction(AI ai, WorldCoordinate destination) {
this.destination = destination;
this.path = ai.navMesh.pathToNearest(ai.getPosition().toWorld(ai.team), destination);
}
@Override
public Result step(AI ai) {
if (this.path.isEmpty()) return Result.FAILED;
WorldCoordinate coordinate = path.get(0);
if (!ai.navMesh.isWalkable(coordinate)) {
path = ai.navMesh.pathToNearest(ai.getPosition().toWorld(ai.team), destination);
if (path.isEmpty()) return Result.FAILED;
coordinate = path.get(0);
}
AI.MoveResult moveResult = ai.checkMove(coordinate);
if (moveResult == AI.MoveResult.OVERDISTANCE) {
if (overDistanceCounter == 5) {
path = ai.navMesh.pathToNearest(ai.getPosition().toWorld(ai.team), destination);
return path.isEmpty() ? Result.FAILED : Result.ONGOING;
}
if (overDistanceCounter++ > 5) {
return Result.FAILED;
}
return Result.ONGOING;
}
overDistanceCounter = 0;
if (moveResult == AI.MoveResult.FALLING) {
return Result.ONGOING;
}
if (moveResult == AI.MoveResult.OK) {
path.remove(0);
ai.move(coordinate);
}
return path.isEmpty() ? Result.FINISHED : Result.ONGOING;
}
}
class InteractAction implements Action {
private final LocalCoordinate destination;
public InteractAction(LocalCoordinate destination) {
this.destination = destination;
}
@Override
public Result step(AI ai) {
ai.interact(destination);
return Result.FINISHED;
}
}
class ReadyAction implements Action {
@Override
public Result step(AI ai) {
ai.setReady();
return Result.FINISHED;
}
}
class PlaceTNTAction implements Action {
private final LocalCoordinate destination;
public PlaceTNTAction(LocalCoordinate destination) {
this.destination = destination;
}
@Override
public Result step(AI ai) {
ai.setTNT(destination);
return Result.FINISHED;
}
}
class LoadCannon extends MultiAction {
public LoadCannon(AI ai, Cannon cannon) {
for (LocalCoordinate tntPosition : cannon.getTntPositions()) {
actions.add(new MoveAction(ai, tntPosition.toWorld(ai.team)));
actions.add(new PlaceTNTAction(tntPosition));
}
LocalCoordinate trigger = cannon.getTriggerPositions().iterator().next();
actions.add(new MoveAction(ai, trigger.toWorld(ai.team)));
actions.add(new InteractAction(trigger));
}
}
}
@@ -0,0 +1,90 @@
/*
* 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.fightsystem.ai;
import de.steamwar.fightsystem.fight.Fight;
import de.steamwar.fightsystem.fight.FightTeam;
import de.steamwar.fightsystem.utils.Region;
import lombok.AllArgsConstructor;
import lombok.Getter;
import org.bukkit.util.NumberConversions;
@Getter
@AllArgsConstructor
public class LocalCoordinate {
private double x;
private double y;
private double z;
public int getBlockX() {
return NumberConversions.floor(x);
}
public int getBlockY() {
return NumberConversions.floor(y);
}
public int getBlockZ() {
return NumberConversions.floor(z);
}
public double distanceSquared(LocalCoordinate coordinate) {
return NumberConversions.square(x - coordinate.x) + NumberConversions.square(y - coordinate.y) + NumberConversions.square(z - coordinate.z);
}
public WorldCoordinate toWorld(FightTeam team) {
// TODO: Fix the of by one blue/red
// Team Red is not correct!
// NightTown is team blue unrotated!
Region extend = team.getExtendRegion();
if(Fight.getUnrotated() == team) {
return new WorldCoordinate(
x + extend.getMinX(),
y + team.getSchemRegion().getMinY(),
z + extend.getMinZ()
);
} else {
return new WorldCoordinate(
extend.getMaxX() - x - 1,
y + team.getSchemRegion().getMinY(),
extend.getMaxZ() - z - 1
);
}
}
public LocalCoordinate add(LocalCoordinate localCoordinate) {
this.x += localCoordinate.x;
this.y += localCoordinate.y;
this.z += localCoordinate.z;
return this;
}
public LocalCoordinate add(double x, double y, double z) {
this.x += x;
this.y += y;
this.z += z;
return this;
}
@Override
public String toString() {
return "Local{" + x + "," + y + "," + z + "}";
}
}
@@ -0,0 +1,402 @@
/*
* 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.fightsystem.ai;
import de.steamwar.entity.RBlockDisplay;
import de.steamwar.entity.REntity;
import de.steamwar.entity.REntityServer;
import de.steamwar.fightsystem.ArenaMode;
import de.steamwar.fightsystem.Config;
import de.steamwar.fightsystem.FightSystem;
import de.steamwar.fightsystem.fight.FightTeam;
import de.steamwar.fightsystem.states.FightState;
import de.steamwar.fightsystem.states.OneShotStateDependent;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.block.data.BlockData;
import org.bukkit.util.BoundingBox;
import org.bukkit.util.NumberConversions;
import org.bukkit.util.Transformation;
import org.bukkit.util.VoxelShape;
import org.joml.AxisAngle4f;
import org.joml.Vector3f;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class NavMesh {
private static final double PLAYER_FALL_DISTANCE = 3;
private static final double PLAYER_JUMP_HEIGHT = 1.25;
private static final double PLAYER_HEIGHT = 1.8;
private static final Cuboid PLAYER_SHADOW = new Cuboid(0.2, 0, 0.2, 0.6, 1, 0.6);
private static final Set<Pos> RELATIVE_BLOCKS = new HashSet<>();
protected static final BlockData PATH_BLOCK = Material.LIME_STAINED_GLASS.createBlockData();
static {
for (int y = (int) -Math.ceil(PLAYER_FALL_DISTANCE); y <= Math.ceil(PLAYER_JUMP_HEIGHT); y++) {
RELATIVE_BLOCKS.add(new Pos(-1, y, 0));
RELATIVE_BLOCKS.add(new Pos(1, y, 0));
RELATIVE_BLOCKS.add(new Pos(0, y, -1));
RELATIVE_BLOCKS.add(new Pos(0, y, 1));
}
RELATIVE_BLOCKS.add(new Pos(0, 1, 0));
RELATIVE_BLOCKS.add(new Pos(0, -1, 0));
}
private FightTeam fightTeam;
private REntityServer server = new REntityServer();
private Map<Pos, Pos> walkable = new HashMap<>();
private Map<Pos, Set<Pos>> connections = new HashMap<>(); // Reverse connections!
@Getter
private boolean ready = false;
public NavMesh(FightTeam fightTeam) {
this.fightTeam = fightTeam;
new OneShotStateDependent(ArenaMode.All, FightState.PostSchemSetup, () -> {
Bukkit.getOnlinePlayers().forEach(server::addPlayer);
Bukkit.getScheduler().runTaskLater(FightSystem.getPlugin(), () -> {
long time = System.currentTimeMillis();
fightTeam.getExtendRegion().forEach((x, y, z) -> {
if (y < fightTeam.getSchemRegion().getMinY()) return;
calculatePosition(new Pos(x, y, z), walkable);
});
walkable.values().forEach(this::checkNeighbouring);
System.out.println("NavMesh initialized in " + (System.currentTimeMillis() - time) + " ms");
ready = true;
}, 20);
});
new OneShotStateDependent(ArenaMode.All, FightState.Spectate, () -> {
ready = false;
});
}
private void calculatePosition(Pos pos, Map<Pos, Pos> walkable) {
Block block = Config.world.getBlockAt(pos.x, pos.y, pos.z);
// Air is not walkable
if (block.isPassable()) return;
// Ignore Ladders from floor calculation!
if (block.getType() != Material.LADDER) {
// Floor Position
pos.floor = playerCollisionMax(block.getCollisionShape());
// If no walkable block remove
if (pos.floor == 0) return;
} else {
pos.floor = 0.5;
}
// Ceiling Position
pos.ceiling = fightTeam.getExtendRegion().getMaxY() - pos.y;
for (int y = pos.y + 1; y <= fightTeam.getExtendRegion().getMaxY() + 3; y++) {
block = Config.world.getBlockAt(pos.x, y, pos.z);
if (block.isPassable()) continue;
double min = playerCollisionMin(block.getCollisionShape());
if (min >= 1.0) continue;
pos.ceiling = y + min - pos.y;
break;
}
// If player cannot fit remove
if (pos.ceiling - pos.floor < PLAYER_HEIGHT) return;
walkable.put(pos, pos);
}
@RequiredArgsConstructor
private static class Pos {
private final int x;
private final int y;
private final int z;
private double floor = 0;
private double ceiling = 0;
@Override
public String toString() {
return x + "," + y + "(" + floor + ".." + ceiling + ")" + "," + z;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Pos)) return false;
Pos pos = (Pos) o;
return x == pos.x && y == pos.y && z == pos.z;
}
@Override
public int hashCode() {
return Objects.hash(x, y, z);
}
public Pos add(Pos pos) {
return new Pos(x + pos.x, y + pos.y, z + pos.z);
}
public WorldCoordinate toWorld() {
return new WorldCoordinate(x, y + floor, z);
}
public double floorPosition() {
return y + floor;
}
public double ceilingPosition() {
return y + ceiling;
}
}
private double playerCollisionMax(VoxelShape voxelShape) {
List<Double> yList = voxelShape.getBoundingBoxes().stream()
.flatMap(boundingBox -> Stream.of(boundingBox.getMinY(), boundingBox.getMaxY()))
.sorted()
.collect(Collectors.toList());
double collisionY = 0;
for (double y : yList) {
if (voxelShape.getBoundingBoxes()
.stream()
.noneMatch(boundingBox -> collides(y, boundingBox))) {
collisionY = y;
break;
}
}
return collisionY;
}
private double playerCollisionMin(VoxelShape voxelShape) {
List<Double> yList = voxelShape.getBoundingBoxes().stream()
.flatMap(boundingBox -> Stream.of(boundingBox.getMinY(), boundingBox.getMaxY()))
.sorted(Comparator.reverseOrder())
.collect(Collectors.toList());
double collisionY = 1.5;
for (double y : yList) {
if (voxelShape.getBoundingBoxes()
.stream()
.noneMatch(boundingBox -> collides(y - 1, boundingBox))) {
collisionY = y;
break;
}
}
return collisionY;
}
private boolean collides(double y, BoundingBox boundingBox) {
Cuboid second = new Cuboid(boundingBox.getMinX(), boundingBox.getMinY(), boundingBox.getMinZ(), boundingBox.getWidthX(), boundingBox.getHeight(), boundingBox.getWidthZ());
return PLAYER_SHADOW.setY(y).intersects(second);
}
@AllArgsConstructor
private static class Cuboid {
private double x;
private double y;
private double z;
private double dx;
private double dy;
private double dz;
public boolean intersects(Cuboid cuboid) {
double minx = x - cuboid.dx;
double miny = y - cuboid.dy;
double minz = z - cuboid.dz;
double maxx = minx + dx + cuboid.dx;
double maxy = miny + dy + cuboid.dy;
double maxz = minz + dz + cuboid.dz;
return maxx > cuboid.x && maxy > cuboid.y && maxz > cuboid.z && minx < cuboid.x && miny < cuboid.y && minz < cuboid.z;
}
public Cuboid setY(double y) {
return new Cuboid(x, y, z, dx, dy, dz);
}
}
public void update(WorldCoordinate coordinate) {
final int px = coordinate.getBlockX();
final int py = coordinate.getBlockY();
final int pz = coordinate.getBlockZ();
for (int x = -2; x <= 2; x++) {
for (int z = -2; z <= 2; z++) {
for (int y = fightTeam.getSchemRegion().getMinY(); y <= py + 2; y++) {
Pos current = new Pos(px + x, y, pz + z);
walkable.remove(current);
calculatePosition(current, walkable);
}
}
}
walkable.values().forEach(this::checkNeighbouring);
}
private void checkNeighbouring(Pos pos) {
for (Set<Pos> value : connections.values()) {
value.remove(pos);
}
for (Pos relative : RELATIVE_BLOCKS) {
Pos other = walkable.get(pos.add(relative));
if (other == null) continue;
Block thisBlock = Config.world.getBlockAt(pos.x, pos.y, pos.z);
Block otherBlock = Config.world.getBlockAt(other.x, other.y, other.z);
if (thisBlock.getType() != Material.LADDER && otherBlock.getType() == Material.LADDER) {
if ((relative.x != 0 || relative.z != 0) && pos.y != other.y) continue;
} else {
// Check if jumpable vertical distance
if (other.floorPosition() - pos.floorPosition() > PLAYER_JUMP_HEIGHT) continue;
// Check if Ceiling is high enough for player walking down!
if (other.ceilingPosition() < pos.floorPosition() + PLAYER_HEIGHT) continue;
}
connections.computeIfAbsent(other, __ -> new LinkedHashSet<>()).add(pos);
}
}
private Pos toPos(WorldCoordinate coordinate) {
Pos pos = new Pos(coordinate.getBlockX(), coordinate.getBlockY(), coordinate.getBlockZ());
if (walkable.containsKey(pos)) return walkable.get(pos);
pos = new Pos(pos.x, pos.y - 1, pos.z);
if (walkable.containsKey(pos)) return walkable.get(pos);
return null;
}
public boolean isWalkable(WorldCoordinate coordinate) {
Pos pos = new Pos(coordinate.getBlockX(), NumberConversions.floor(coordinate.getY() - 0.0625), coordinate.getBlockZ());
return walkable.containsKey(pos);
}
public List<WorldCoordinate> walkable() {
return walkable.values().stream().map(Pos::toWorld).collect(Collectors.toList());
}
public List<WorldCoordinate> pathToNearest(WorldCoordinate fromCoordinate, WorldCoordinate toCoordinate) {
Pos to = toPos(toCoordinate);
if (walkable.containsKey(to)) return path(toPos(fromCoordinate), to);
Pos closestTo = walkable.values()
.stream()
.filter(pos -> {
double distance = pos.toWorld().distanceSquared(toCoordinate);
return distance < AI.INTERACTION_RANGE * AI.INTERACTION_RANGE && distance > 1.5 * 1.5;
})
.min(Comparator.comparing(pos -> pos.toWorld().distanceSquared(toCoordinate)))
.orElse(null);
if (closestTo == null) {
return Collections.emptyList();
}
return path(toPos(fromCoordinate), closestTo);
}
public List<WorldCoordinate> path(WorldCoordinate fromCoordinate, WorldCoordinate toCoordinate) {
Pos from = toPos(fromCoordinate);
Pos to = toPos(toCoordinate);
return path(from, to);
}
private List<WorldCoordinate> path(Pos from, Pos to) {
server.getEntities().forEach(REntity::die);
if (from == null || to == null) {
return Collections.emptyList();
}
if (from.equals(to)) {
return Collections.emptyList();
}
List<Pos> checking = new ArrayList<>(Arrays.asList(to));
Map<Pos, Pos> route = new HashMap<>();
while (!checking.isEmpty()) {
Set<Pos> toCheck = new HashSet<>();
for (Pos pos : checking) {
boolean foundFrom = false;
Set<Pos> successors = connections.get(pos);
if (successors == null) {
return Collections.emptyList();
}
for (Pos p : successors) {
if (route.containsKey(p)) continue;
route.put(p, pos);
toCheck.add(p);
foundFrom = p.equals(from);
if (foundFrom) break;
}
if (foundFrom) {
List<Pos> path = new ArrayList<>();
path.add(from);
while (path.get(path.size() - 1) != to) {
path.add(route.get(path.get(path.size() - 1)));
}
for (int i = 0; i < path.size(); i++) {
path.set(i, walkable.get(path.get(i)));
}
int i = 0;
while (i < path.size() - 1) {
Pos current = path.get(i);
Pos next = path.get(i + 1);
if (current.floorPosition() > next.floorPosition() + 1) {
Pos between = new Pos(next.x, current.y, next.z);
between.floor = current.floor;
path.add(i + 1, between);
i++;
}
i++;
}
List<WorldCoordinate> coordinates = path.stream().skip(1).map(Pos::toWorld).collect(Collectors.toList());
coordinates.forEach(coordinate -> {
RBlockDisplay block = new RBlockDisplay(server, coordinate.toLocation(Config.world));
block.setBlock(PATH_BLOCK);
block.setTransform(new Transformation(new Vector3f(0, 0, 0), new AxisAngle4f(0, 0, 0, 0), new Vector3f(1, 0.001F, 1), new AxisAngle4f(0, 0, 0, 0)));
});
coordinates.forEach(vector -> vector.add(0.5, 0, 0.5));
return coordinates;
}
}
checking.clear();
checking.addAll(toCheck);
}
return Collections.emptyList();
}
}
@@ -0,0 +1,107 @@
/*
* 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.fightsystem.ai;
import de.steamwar.fightsystem.fight.Fight;
import de.steamwar.fightsystem.fight.FightTeam;
import de.steamwar.fightsystem.utils.Region;
import lombok.AllArgsConstructor;
import lombok.Getter;
import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.util.NumberConversions;
import org.bukkit.util.Vector;
@Getter
@AllArgsConstructor
public class WorldCoordinate {
private double x;
private double y;
private double z;
public static WorldCoordinate from(Location location) {
return new WorldCoordinate(location.getX(), location.getY(), location.getZ());
}
public static WorldCoordinate from(Vector vector) {
return new WorldCoordinate(vector.getX(), vector.getY(), vector.getZ());
}
public int getBlockX() {
return NumberConversions.floor(x);
}
public int getBlockY() {
return NumberConversions.floor(y);
}
public int getBlockZ() {
return NumberConversions.floor(z);
}
public double distanceSquared(WorldCoordinate coordinate) {
return NumberConversions.square(x - coordinate.x) + NumberConversions.square(y - coordinate.y) + NumberConversions.square(z - coordinate.z);
}
public LocalCoordinate toLocal(FightTeam team) {
// TODO: Fix the of by one blue/red
Region extend = team.getExtendRegion();
if (Fight.getUnrotated() == team) {
return new LocalCoordinate(
x - extend.getMinX(),
y - team.getSchemRegion().getMinY(),
z - extend.getMinZ()
);
} else {
return new LocalCoordinate(
extend.getMaxX() - x + 1,
y - team.getSchemRegion().getMinY(),
extend.getMaxZ() - z + 1
);
}
}
public WorldCoordinate add(WorldCoordinate worldCoordinate) {
this.x += worldCoordinate.x;
this.y += worldCoordinate.y;
this.z += worldCoordinate.z;
return this;
}
public WorldCoordinate add(double x, double y, double z) {
this.x += x;
this.y += y;
this.z += z;
return this;
}
public Vector toVector() {
return new Vector(x, y, z);
}
public Location toLocation(World world) {
return new Location(world, x, y, z);
}
@Override
public String toString() {
return "World{" + x + "," + y + "," + z + "}";
}
}
@@ -0,0 +1,45 @@
/*
* 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.fightsystem.ai.schematic;
import de.steamwar.fightsystem.ai.LocalCoordinate;
import lombok.Getter;
import java.util.HashSet;
import java.util.Set;
@Getter
public class Bridge {
private final Set<LocalCoordinate> shieldActivators = new HashSet<>();
private LocalCoordinate automaticActivator = null;
private int automaticActivatorTime = 0;
public Bridge addShieldActivator(LocalCoordinate shieldActivator) {
this.shieldActivators.add(shieldActivator);
return this;
}
public Bridge addAutomaticActivator(LocalCoordinate automaticActivator, int automaticActivatorTime) {
this.automaticActivator = automaticActivator;
this.automaticActivatorTime = automaticActivatorTime;
return this;
}
}
@@ -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,19 +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.fightsystem.listener; package de.steamwar.fightsystem.ai.schematic;
import de.steamwar.core.VersionDependent; import de.steamwar.fightsystem.ai.LocalCoordinate;
import de.steamwar.fightsystem.FightSystem; import lombok.Getter;
import de.steamwar.linkage.Linked;
@Linked import java.util.ArrayList;
public class WindchargeStopper { import java.util.HashSet;
import java.util.List;
import java.util.Set;
static { @Getter
VersionDependent.getVersionImpl(FightSystem.getPlugin()); public class Cannon {
private final List<LocalCoordinate> tntPositions = new ArrayList<>();
private final Set<LocalCoordinate> triggerPositions = new HashSet<>();
public Cannon addTnt(LocalCoordinate tnt) {
tntPositions.add(tnt);
return this;
} }
public interface IWindchargeStopper { public Cannon addTrigger(LocalCoordinate trigger) {
triggerPositions.add(trigger);
return this;
} }
} }
@@ -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.fightsystem.ai.schematic;
import de.steamwar.fightsystem.ai.LocalCoordinate;
import lombok.Getter;
import org.bukkit.util.Consumer;
import java.util.ArrayList;
import java.util.List;
@Getter
public class WarMachine {
private int schematicId = 0;
private Bridge bridge = new Bridge();
private List<Cannon> cannons = new ArrayList<>();
private List<LocalCoordinate> tntChests = new ArrayList<>();
public WarMachine setSchematicId(int schematicId) {
this.schematicId = schematicId;
return this;
}
public WarMachine editBridge(Consumer<Bridge> initializer) {
initializer.accept(bridge);
return this;
}
public WarMachine addCannon(Consumer<Cannon> initializer) {
Cannon cannon = new Cannon();
initializer.accept(cannon);
cannons.add(cannon);
return this;
}
public WarMachine addTnTChest(LocalCoordinate chest) {
tntChests.add(chest);
return this;
}
public WarMachine finish(List<WarMachine> list) {
list.add(this);
return this;
}
}
@@ -0,0 +1,71 @@
/*
* 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.fightsystem.ai.schematic.impl;
import de.steamwar.fightsystem.ai.LocalCoordinate;
import de.steamwar.fightsystem.ai.schematic.WarMachine;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
public class MiniWarGear20 {
private static final Random RANDOM = new Random();
private static final List<WarMachine> MiniWarGear20 = new ArrayList<>();
public static WarMachine select() {
return MiniWarGear20.get(RANDOM.nextInt(MiniWarGear20.size()));
}
public static WarMachine select(int schematicId) {
return MiniWarGear20.stream()
.filter(warMachine -> warMachine.getSchematicId() == schematicId)
.findFirst()
.orElse(null);
}
public static final WarMachine DPR_PV1_Reaper = new WarMachine()
.setSchematicId(135745)
.editBridge(bridge -> {
bridge.addShieldActivator(new LocalCoordinate(22, 15, 18));
bridge.addShieldActivator(new LocalCoordinate(31, 3, 12));
bridge.addShieldActivator(new LocalCoordinate(19, 3, 12));
})
.addCannon(cannon -> {
cannon.addTnt(new LocalCoordinate(29, 1, 21));
cannon.addTnt(new LocalCoordinate(29, 1, 22));
cannon.addTnt(new LocalCoordinate(29, 1, 23));
cannon.addTnt(new LocalCoordinate(30, 1, 22));
cannon.addTnt(new LocalCoordinate(30, 1, 23));
cannon.addTnt(new LocalCoordinate(29, 2, 24));
cannon.addTnt(new LocalCoordinate(30, 2, 24));
cannon.addTnt(new LocalCoordinate(29, 2, 25));
cannon.addTnt(new LocalCoordinate(30, 2, 25));
cannon.addTnt(new LocalCoordinate(29, 3, 24));
cannon.addTnt(new LocalCoordinate(30, 3, 24));
cannon.addTnt(new LocalCoordinate(29, 3, 25));
cannon.addTnt(new LocalCoordinate(30, 3, 25));
cannon.addTrigger(new LocalCoordinate(30, 3, 19));
cannon.addTrigger(new LocalCoordinate(31, 3, 18));
cannon.addTrigger(new LocalCoordinate(31, 3, 20));
})
.finish(MiniWarGear20);
}
@@ -0,0 +1,98 @@
/*
* 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.fightsystem.ai.yoyonow;
import com.sk89q.worldedit.extent.clipboard.Clipboard;
import de.steamwar.fightsystem.ai.AI;
import de.steamwar.fightsystem.ai.Action;
import de.steamwar.fightsystem.ai.schematic.Cannon;
import de.steamwar.fightsystem.ai.schematic.WarMachine;
import de.steamwar.fightsystem.ai.schematic.impl.MiniWarGear20;
import de.steamwar.fightsystem.fight.FightTeam;
import de.steamwar.fightsystem.states.FightState;
import de.steamwar.sql.SchematicNode;
import de.steamwar.sql.SteamwarUser;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
public class YoyoNowAI extends AI {
private WarMachine selectedSchematic;
protected YoyoNowAI(FightTeam team) {
super(team, SteamwarUser.get("YoyoNow.AI"));
getEntity().setGlowing(true);
}
@Override
public SchematicNode chooseSchematic() {
selectedSchematic = MiniWarGear20.select();
return SchematicNode.getSchematicNode(selectedSchematic.getSchematicId());
}
@Override
public void schematic(Clipboard clipboard) {
selectedSchematic = MiniWarGear20.select(team.getSchematic());
if (selectedSchematic == null) stop();
}
private final Random random = new Random();
private List<Action> actions = new ArrayList<>();
private boolean setupDone = false;
@Override
protected void plan() {
if (!navMesh.isReady()) return;
if (!actions.isEmpty()) {
Action.Result result = actions.get(0).step(this);
if (result == Action.Result.FAILED) {
chat(actions.get(0) + " failed");
actions.clear();
return;
}
if (result == Action.Result.FINISHED) {
chat(actions.get(0) + " finished");
actions.remove(0);
}
return;
}
if (!setupDone && FightState.getFightState() == FightState.POST_SCHEM_SETUP) {
chat("Setup actions to perform!");
selectedSchematic.getBridge().getShieldActivators().forEach(localCoordinate -> {
actions.add(new Action.MoveAction(this, localCoordinate.toWorld(getTeam())));
actions.add(new Action.InteractAction(localCoordinate));
});
actions.add(new Action.ReadyAction());
chat("Actions: " + actions.size());
setupDone = true;
}
if (FightState.getFightState() == FightState.RUNNING) {
Cannon cannon = selectedSchematic.getCannons().get(random.nextInt(selectedSchematic.getCannons().size()));
actions.add(new Action.LoadCannon(this, cannon));
}
}
}
@@ -181,12 +181,7 @@ public class Permanent implements Listener {
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onExplosion(EntityExplodeEvent e) { public void onExplosion(EntityExplodeEvent e) {
if (!(e.getEntity() instanceof TNTPrimed)) { if (!(e.getEntity() instanceof TNTPrimed)) return;
if (Config.GameModeConfig.Schematic.Type.toDB().equals("wargearseason26")) {
e.blockList().clear();
}
return;
}
if (!Config.GameModeConfig.Arena.WaterDamage) return; if (!Config.GameModeConfig.Arena.WaterDamage) return;
e.blockList().removeIf(block -> { e.blockList().removeIf(block -> {
if(block.getType() == Material.TNT) { if(block.getType() == Material.TNT) {
@@ -35,7 +35,10 @@ import org.bukkit.entity.HumanEntity;
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.inventory.*; import org.bukkit.event.inventory.InventoryAction;
import org.bukkit.event.inventory.InventoryClickEvent;
import org.bukkit.event.inventory.InventoryCloseEvent;
import org.bukkit.event.inventory.InventoryOpenEvent;
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 org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemStack;
@@ -78,8 +81,25 @@ public class PersonalKitCreator implements Listener {
if(!openKitCreators.containsKey(e.getWhoClicked())) if(!openKitCreators.containsKey(e.getWhoClicked()))
return; return;
Player player = (Player) e.getWhoClicked();
//Deny bad items
if(Kit.isBadItem(e.getCursor())) if(Kit.isBadItem(e.getCursor()))
e.setCancelled(true); e.setCancelled(true);
/* Should the inventory reset? */
if(e.getAction() != InventoryAction.PLACE_ALL)
return;
ItemStack[] items = e.getWhoClicked().getInventory().getContents();
for(int i = 0; i < items.length; i++){
ItemStack stack = items[i];
if(stack != null && i != e.getSlot())
return;
}
FightPlayer fightPlayer = Fight.getFightPlayer(player);
assert fightPlayer != null;
Bukkit.getScheduler().runTaskLater(FightSystem.getPlugin(), () -> fightPlayer.getKit().loadToPlayer(player), 1);
} }
@EventHandler @EventHandler
@@ -97,10 +117,7 @@ public class PersonalKitCreator implements Listener {
if(backup == null) if(backup == null)
return; return;
InventoryType type = e.getInventory().getType(); backup.close();
if(type != InventoryType.PLAYER && type != InventoryType.CREATIVE) {
backup.close();
}
} }
@EventHandler @EventHandler
@@ -109,7 +126,7 @@ public class PersonalKitCreator implements Listener {
if(backup == null) if(backup == null)
return; return;
Bukkit.getScheduler().runTaskLater(FightSystem.getPlugin(), backup::close, 1); backup.close();
} }
@EventHandler @EventHandler
@@ -134,11 +151,9 @@ public class PersonalKitCreator implements Listener {
} }
private void close(){ private void close(){
openKitCreators.remove(player);
Kit kit1 = new Kit(kit.getName(), player); Kit kit1 = new Kit(kit.getName(), player);
kit1.removeBadItems(); kit1.removeBadItems();
openKitCreators.remove(player);
kit1.toPersonalKit(kit); kit1.toPersonalKit(kit);
backup.loadToPlayer(player); backup.loadToPlayer(player);
player.setGameMode(GameMode.SURVIVAL); player.setGameMode(GameMode.SURVIVAL);
+11 -1
View File
@@ -91,4 +91,14 @@ tasks.register<FightServer>("QuickGear20") {
template = "QuickGear20" template = "QuickGear20"
worldName = "arenas/WarGearPark" worldName = "arenas/WarGearPark"
config = "QuickGear20.yml" config = "QuickGear20.yml"
} }
tasks.register<FightServer>("MiniWarGear20") {
group = "run"
description = "Run a MiniWarGear 1.20 Fight Server"
dependsOn(":SpigotCore:shadowJar")
dependsOn(":FightSystem:shadowJar")
template = "MiniWarGear20"
worldName = "arenas/NightTown"
config = "MiniWarGear20.yml"
}
@@ -1,37 +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/>.
*/
plugins {
steamwar.java
}
dependencies {
compileOnly(project(":SpigotCore", "default"))
compileOnly(project(":SchematicSystem:SchematicSystem_Core", "default"))
compileOnly(libs.paperapi21) {
attributes {
// Very Hacky, but it works
attribute(TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE, 21)
}
}
compileOnly(libs.nms21)
compileOnly(libs.fawe21)
}
@@ -1,137 +0,0 @@
/*
* 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.schematicsystem.autocheck;
import com.sk89q.jnbt.CompoundTag;
import com.sk89q.worldedit.entity.Entity;
import com.sk89q.worldedit.extent.clipboard.Clipboard;
import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.world.block.BaseBlock;
import de.steamwar.core.Core;
import de.steamwar.sql.GameModeConfig;
import org.bukkit.Material;
import java.util.*;
import java.util.stream.Collectors;
public class AutoChecker21 implements AutoChecker.IAutoChecker {
public AutoChecker.BlockScanResult scan(Clipboard clipboard, GameModeConfig<Material, String> type) {
AutoChecker.BlockScanResult result = new AutoChecker.BlockScanResult();
BlockVector3 min = clipboard.getMinimumPoint();
BlockVector3 max = clipboard.getMaximumPoint();
for (int x = min.getBlockX(); x <= max.getBlockX(); x++) {
for (int y = min.getBlockY(); y <= max.getBlockY(); y++) {
for (int z = min.getBlockZ(); z <= max.getBlockZ(); z++) {
final BaseBlock block = clipboard.getFullBlock(BlockVector3.at(x, y, z));
final Material material = Material.matchMaterial(block.getBlockType().getId());
if (material == null) {
continue;
}
result.getBlockCounts().merge(material, 1, Integer::sum);
if (AutoCheckerItems.impl.getInventoryMaterials().contains(material)) {
checkInventory(result, block, material, new BlockPos(x, y, z), type);
}
if (x == min.getBlockX() || x == max.getBlockX() || y == max.getBlockY() || z == min.getBlockZ() || z == max.getBlockZ()) {
result.getDesignBlocks().computeIfAbsent(material, m -> new ArrayList<>()).add(new BlockPos(x, y, z));
}
}
}
}
return result;
}
private static final Map<Material, Set<Material>> itemsInInv = new EnumMap<>(Material.class);
static {
itemsInInv.put(Material.BUCKET, EnumSet.of(Material.DISPENSER));
itemsInInv.put(Material.TNT, EnumSet.of(Material.CHEST, Material.BARREL, Material.SHULKER_BOX, Material.BLACK_SHULKER_BOX, Material.BLUE_SHULKER_BOX,
Material.BROWN_SHULKER_BOX, Material.CYAN_SHULKER_BOX, Material.GRAY_SHULKER_BOX, Material.GREEN_SHULKER_BOX, Material.LIGHT_BLUE_SHULKER_BOX,
Material.LIGHT_GRAY_SHULKER_BOX, Material.LIME_SHULKER_BOX, Material.MAGENTA_SHULKER_BOX, Material.ORANGE_SHULKER_BOX,
Material.PINK_SHULKER_BOX, Material.PURPLE_SHULKER_BOX, Material.RED_SHULKER_BOX, Material.WHITE_SHULKER_BOX, Material.YELLOW_SHULKER_BOX));
itemsInInv.put(Material.FIRE_CHARGE, EnumSet.of(Material.DISPENSER));
itemsInInv.put(Material.ARROW, EnumSet.of(Material.DISPENSER));
AutoCheckerItems.impl.getAllowedMaterialsInInventory().forEach(material -> itemsInInv.put(material, AutoCheckerItems.impl.getInventoryMaterials()));
}
private void checkInventory(AutoChecker.BlockScanResult result, BaseBlock block, Material material, BlockPos pos, GameModeConfig<Material, String> type) {
CompoundTag nbt = block.getNbtData();
if (nbt == null) {
result.getDefunctNbt().add(pos);
return;
}
if (material == Material.JUKEBOX && nbt.getValue().containsKey("RecordItem")) {
result.getRecords().add(pos);
return;
}
List<CompoundTag> items = nbt.getList("Items", CompoundTag.class);
if (items.isEmpty())
return; // Leeres Inventar
int counter = 0;
int windChargeCount = 0;
for (CompoundTag item : items) {
if (!item.containsKey("id")) {
result.getDefunctNbt().add(pos);
continue;
}
Material itemType = Material.matchMaterial(item.getString("id"));
if (itemType == null) // Leere Slots
continue;
if(type.Schematic.Type.getName().equals("wargearseason26") && material == Material.DISPENSER && itemType == Material.WIND_CHARGE) {
windChargeCount += item.getInt("count");
}
else if (!itemsInInv.getOrDefault(itemType, EnumSet.noneOf(Material.class)).contains(material)) {
result.getForbiddenItems().computeIfAbsent(pos, blockVector3 -> new HashSet<>()).add(itemType);
} else if (material == Material.DISPENSER && (itemType == Material.ARROW || itemType == Material.FIRE_CHARGE)) {
counter += Core.getVersion() >= 21 ? item.getInt("count") : item.getByte("Count");
}
if (item.containsKey("tag")) {
result.getForbiddenNbt().computeIfAbsent(pos, blockVector3 -> new HashSet<>()).add(itemType);
}
}
result.getDispenserItems().put(pos, counter);
result.getWindChargeCount().put(pos, windChargeCount);
}
@Override
public AutoCheckerResult check(Clipboard clipboard, GameModeConfig<Material, String> type) {
return AutoCheckerResult.builder().type(type).height(clipboard.getDimensions().getBlockY()).width(clipboard.getDimensions().getBlockX())
.depth(clipboard.getDimensions().getBlockZ()).blockScanResult(scan(clipboard, type))
.entities(clipboard.getEntities().stream().map(Entity::getLocation)
.map(blockVector3 -> new BlockPos(blockVector3.getBlockX(), blockVector3.getBlockY(), blockVector3.getBlockZ()))
.collect(Collectors.toList()))
.build();
}
@Override
public AutoCheckerResult sizeCheck(Clipboard clipboard, GameModeConfig<Material, String> type) {
return AutoCheckerResult.builder().type(type).height(clipboard.getDimensions().getBlockY()).width(clipboard.getDimensions().getBlockX())
.depth(clipboard.getDimensions().getBlockZ()).build();
}
}
@@ -1,49 +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.schematicsystem.autocheck;
import org.bukkit.Material;
import java.util.EnumSet;
import java.util.Set;
public class AutoCheckerItems21 implements AutoCheckerItems {
private static final Set<Material> INVENTORY = EnumSet.of(Material.BARREL, Material.BLAST_FURNACE, Material.BREWING_STAND, Material.CAMPFIRE,
Material.CHEST, Material.DISPENSER, Material.DROPPER, Material.FURNACE, Material.HOPPER, Material.JUKEBOX, Material.SHULKER_BOX,
Material.WHITE_SHULKER_BOX, Material.ORANGE_SHULKER_BOX, Material.MAGENTA_SHULKER_BOX, Material.LIGHT_BLUE_SHULKER_BOX, Material.YELLOW_SHULKER_BOX,
Material.LIME_SHULKER_BOX, Material.PINK_SHULKER_BOX, Material.GRAY_SHULKER_BOX, Material.LIGHT_GRAY_SHULKER_BOX, Material.CYAN_SHULKER_BOX,
Material.PURPLE_SHULKER_BOX, Material.BLUE_SHULKER_BOX, Material.BROWN_SHULKER_BOX, Material.GREEN_SHULKER_BOX, Material.RED_SHULKER_BOX,
Material.BLACK_SHULKER_BOX, Material.SMOKER, Material.TRAPPED_CHEST);
private static final Set<Material> FLOWERS = EnumSet.of(Material.CORNFLOWER, Material.POPPY, Material.FERN, Material.DANDELION, Material.BLUE_ORCHID,
Material.ALLIUM, Material.AZURE_BLUET, Material.RED_TULIP, Material.ORANGE_TULIP, Material.WHITE_TULIP, Material.PINK_TULIP, Material.OXEYE_DAISY,
Material.LILY_OF_THE_VALLEY, Material.WITHER_ROSE, Material.SUNFLOWER, Material.DIAMOND_HORSE_ARMOR, Material.IRON_HORSE_ARMOR,
Material.GOLDEN_HORSE_ARMOR, Material.LEATHER_HORSE_ARMOR, Material.HONEY_BOTTLE, Material.LILAC, Material.ROSE_BUSH, Material.PEONY,
Material.TALL_GRASS, Material.LARGE_FERN);
@Override
public Set<Material> getInventoryMaterials() {
return INVENTORY;
}
@Override
public Set<Material> getAllowedMaterialsInInventory() {
return FLOWERS;
}
}
@@ -262,8 +262,6 @@ AUTO_CHECKER_RESULT_BLOCKS=§7Blocks: §c{0}§7, Max: §e{1}
AUTO_CHECKER_RESULT_UNKNOWN_MATERIAL=§7Unknown block: §c{0} AUTO_CHECKER_RESULT_UNKNOWN_MATERIAL=§7Unknown block: §c{0}
AUTO_CHECKER_RESULT_TOO_MANY_BLOCK=§7{0}: §c{1}§7, Max: §e{2} AUTO_CHECKER_RESULT_TOO_MANY_BLOCK=§7{0}: §c{1}§7, Max: §e{2}
AUTO_CHECKER_RESULT_FORBIDDEN_BLOCK=§7Forbidden block: §c{0} AUTO_CHECKER_RESULT_FORBIDDEN_BLOCK=§7Forbidden block: §c{0}
AUTO_CHECKER_RESULT_WIND_CHARGES=§7Windcharges: §c{0}§7, Max: §e2048
AUTO_CHECKER_RESULT_WIND_CHARGES_DISPENSER=§7Dispenser: §c[{0}, {1}, {2}]§7, Windcharges: §c{3}§7
AUTO_CHECKER_RESULT_FORBIDDEN_ITEM=§7Forbidden Item: [{0}, {1}, {2}] -> §c{3} AUTO_CHECKER_RESULT_FORBIDDEN_ITEM=§7Forbidden Item: [{0}, {1}, {2}] -> §c{3}
AUTO_CHECKER_RESULT_DEFUNCT_NBT=§7Defunct NBT: §7[{0}, {1}, {2}] AUTO_CHECKER_RESULT_DEFUNCT_NBT=§7Defunct NBT: §7[{0}, {1}, {2}]
AUTO_CHECKER_RESULT_DESIGN_BLOCK=§7{0} in Design: [{1}, {2}, {3}] AUTO_CHECKER_RESULT_DESIGN_BLOCK=§7{0} in Design: [{1}, {2}, {3}]
@@ -242,8 +242,6 @@ AUTO_CHECKER_RESULT_BLOCKS=§7Blöcke: §c{0}§7, Max: §e{1}
AUTO_CHECKER_RESULT_UNKNOWN_MATERIAL=§7Unbekannter Block: §c{0} AUTO_CHECKER_RESULT_UNKNOWN_MATERIAL=§7Unbekannter Block: §c{0}
AUTO_CHECKER_RESULT_TOO_MANY_BLOCK=§7{0}: §c{1}§7, Max: §e{2} AUTO_CHECKER_RESULT_TOO_MANY_BLOCK=§7{0}: §c{1}§7, Max: §e{2}
AUTO_CHECKER_RESULT_FORBIDDEN_BLOCK=§7Verbotener Block: §c{0} AUTO_CHECKER_RESULT_FORBIDDEN_BLOCK=§7Verbotener Block: §c{0}
AUTO_CHECKER_RESULT_WIND_CHARGES=§7Windcharges: §c{0}§7, Max: §e2048
AUTO_CHECKER_RESULT_WIND_CHARGES_DISPENSER=§7Werfer: §c[{0}, {1}, {2}]§7, Windcharges: §c{3}§7
AUTO_CHECKER_RESULT_FORBIDDEN_ITEM=§7Verbotener gegenstand: [{0}, {1}, {2}] -> §c{3} AUTO_CHECKER_RESULT_FORBIDDEN_ITEM=§7Verbotener gegenstand: [{0}, {1}, {2}] -> §c{3}
AUTO_CHECKER_RESULT_DEFUNCT_NBT=§7Keine NBT-Daten: §c[{0}, {1}, {2}] AUTO_CHECKER_RESULT_DEFUNCT_NBT=§7Keine NBT-Daten: §c[{0}, {1}, {2}]
AUTO_CHECKER_RESULT_DESIGN_BLOCK=§7{0} im Design: [{1}, {2}, {3}] AUTO_CHECKER_RESULT_DESIGN_BLOCK=§7{0} im Design: [{1}, {2}, {3}]
@@ -55,7 +55,6 @@ public class AutoChecker {
private final List<BlockPos> records = new ArrayList<>(); private final List<BlockPos> records = new ArrayList<>();
private final Map<Material, List<BlockPos>> designBlocks = new EnumMap<>(Material.class); private final Map<Material, List<BlockPos>> designBlocks = new EnumMap<>(Material.class);
private final Map<BlockPos, Integer> dispenserItems = new HashMap<>(); private final Map<BlockPos, Integer> dispenserItems = new HashMap<>();
private final Map<BlockPos, Integer> windChargeCount = new HashMap<>();
private final Map<BlockPos, Set<Material>> forbiddenItems = new HashMap<>(); private final Map<BlockPos, Set<Material>> forbiddenItems = new HashMap<>();
private final Map<BlockPos, Set<Material>> forbiddenNbt = new HashMap<>(); private final Map<BlockPos, Set<Material>> forbiddenNbt = new HashMap<>();
} }
@@ -52,7 +52,6 @@ public class AutoCheckerResult {
isBlockCountOk() && isBlockCountOk() &&
isLimitedBlocksOK() && isLimitedBlocksOK() &&
isDispenserItemsOK() && isDispenserItemsOK() &&
isWindchargeCountOK() &&
!type.isAfterDeadline() && !type.isAfterDeadline() &&
entities.isEmpty() && entities.isEmpty() &&
isDesignBlastResistanceOK(); isDesignBlastResistanceOK();
@@ -63,18 +62,8 @@ public class AutoCheckerResult {
!type.isAfterDeadline(); !type.isAfterDeadline();
} }
public boolean isWindchargeCountOK() {
if( type.Schematic.Type.getName().equals("wargearseason26")) {
int windChargesCount = blockScanResult.getWindChargeCount().values().stream().reduce(Integer::sum).orElse(0);
return windChargesCount <= 2048;
}
else {
return true;
}
}
public boolean isDispenserItemsOK() { public boolean isDispenserItemsOK() {
return blockScanResult.getDispenserItems().values().stream().allMatch(i -> i <= type.Schematic.MaxDispenserItems); return blockScanResult.getDispenserItems().values().stream().allMatch(i -> i <= type.Schematic.MaxDispenserItems);
} }
public boolean hasWarnings() { public boolean hasWarnings() {
@@ -138,19 +127,6 @@ public class AutoCheckerResult {
} }
}); });
} }
if(!isWindchargeCountOK()) {
int windChargesCount = blockScanResult.getWindChargeCount().values().stream().reduce(Integer::sum).orElse(0);
SchematicSystem.MESSAGE.sendPrefixless("AUTO_CHECKER_RESULT_WIND_CHARGES", p, windChargesCount, 2048);
blockScanResult.getWindChargeCount().entrySet().stream().filter(blockVector3IntegerEntry -> blockVector3IntegerEntry.getValue() > 0).forEach(blockVector3IntegerEntry -> {
SchematicSystem.MESSAGE.sendPrefixless("AUTO_CHECKER_RESULT_WIND_CHARGES_DISPENSER", p, SchematicSystem.MESSAGE.parse("AUTO_CHECKER_RESULT_TELEPORT_HERE", p), tpCommandTo(blockVector3IntegerEntry.getKey()),
blockVector3IntegerEntry.getKey().getBlockX(),
blockVector3IntegerEntry.getKey().getBlockY(),
blockVector3IntegerEntry.getKey().getBlockZ(),
blockVector3IntegerEntry.getValue());
});
}
blockScanResult.getDispenserItems().entrySet().stream().filter(blockVector3IntegerEntry -> blockVector3IntegerEntry.getValue() > type.Schematic.MaxDispenserItems).forEach(blockVector3IntegerEntry -> { blockScanResult.getDispenserItems().entrySet().stream().filter(blockVector3IntegerEntry -> blockVector3IntegerEntry.getValue() > type.Schematic.MaxDispenserItems).forEach(blockVector3IntegerEntry -> {
SchematicSystem.MESSAGE.sendPrefixless("AUTO_CHECKER_RESULT_TOO_MANY_DISPENSER_ITEMS", p, SchematicSystem.MESSAGE.parse("AUTO_CHECKER_RESULT_TELEPORT_HERE", p), tpCommandTo(blockVector3IntegerEntry.getKey()), SchematicSystem.MESSAGE.sendPrefixless("AUTO_CHECKER_RESULT_TOO_MANY_DISPENSER_ITEMS", p, SchematicSystem.MESSAGE.parse("AUTO_CHECKER_RESULT_TELEPORT_HERE", p), tpCommandTo(blockVector3IntegerEntry.getKey()),
blockVector3IntegerEntry.getKey().getBlockX(), blockVector3IntegerEntry.getKey().getBlockX(),
@@ -159,7 +135,6 @@ public class AutoCheckerResult {
blockVector3IntegerEntry.getValue(), blockVector3IntegerEntry.getValue(),
type.Schematic.MaxDispenserItems); type.Schematic.MaxDispenserItems);
}); });
blockScanResult.getRecords().forEach(blockVector3 -> { blockScanResult.getRecords().forEach(blockVector3 -> {
SchematicSystem.MESSAGE.sendPrefixless("AUTO_CHECKER_RESULT_RECORD", p, SchematicSystem.MESSAGE.parse("AUTO_CHECKER_RESULT_TELEPORT_HERE", p), tpCommandTo(blockVector3), blockVector3.getBlockX(), blockVector3.getBlockY(), blockVector3.getBlockZ()); SchematicSystem.MESSAGE.sendPrefixless("AUTO_CHECKER_RESULT_RECORD", p, SchematicSystem.MESSAGE.parse("AUTO_CHECKER_RESULT_TELEPORT_HERE", p), tpCommandTo(blockVector3), blockVector3.getBlockX(), blockVector3.getBlockY(), blockVector3.getBlockZ());
}); });
-1
View File
@@ -32,5 +32,4 @@ dependencies {
implementation(project(":SchematicSystem:SchematicSystem_15")) implementation(project(":SchematicSystem:SchematicSystem_15"))
implementation(project(":SchematicSystem:SchematicSystem_19")) implementation(project(":SchematicSystem:SchematicSystem_19"))
implementation(project(":SchematicSystem:SchematicSystem_20")) implementation(project(":SchematicSystem:SchematicSystem_20"))
implementation(project(":SchematicSystem:SchematicSystem_21"))
} }
@@ -93,7 +93,7 @@ public class ChunkHider18 implements ChunkHider {
private static final Reflection.Method getKey = Reflection.getTypedMethod(IRegistry.class, null, MinecraftKey.class, Object.class); private static final Reflection.Method getKey = Reflection.getTypedMethod(IRegistry.class, null, MinecraftKey.class, Object.class);
private static final Reflection.Method getName = Reflection.getTypedMethod(MinecraftKey.class, null, String.class); private static final Reflection.Method getName = Reflection.getTypedMethod(MinecraftKey.class, null, String.class);
protected boolean tileEntityVisible(Set<String> hiddenBlockEntities, Object tile) { protected boolean tileEntityVisible(Set<String> hiddenBlockEntities, Object tile) {
return !hiddenBlockEntities.contains(TechHider.normalizeBlockEntityId((String) getName.invoke(getKey.invoke(tileEntityTypes, entityType.get(tile))))); return !hiddenBlockEntities.contains((String) getName.invoke(getKey.invoke(tileEntityTypes, entityType.get(tile))));
} }
private void blocks(SectionHider section) { private void blocks(SectionHider section) {
@@ -20,11 +20,8 @@
package de.steamwar.techhider; package de.steamwar.techhider;
import de.steamwar.Reflection; import de.steamwar.Reflection;
import de.steamwar.core.Core;
import net.minecraft.core.IRegistry;
import net.minecraft.core.SectionPosition; import net.minecraft.core.SectionPosition;
import net.minecraft.network.protocol.game.PacketPlayOutBlockBreak; import net.minecraft.network.protocol.game.PacketPlayOutBlockBreak;
import net.minecraft.resources.MinecraftKey;
import net.minecraft.world.level.block.entity.TileEntitySign; import net.minecraft.world.level.block.entity.TileEntitySign;
import net.minecraft.world.level.block.entity.TileEntityTypes; import net.minecraft.world.level.block.entity.TileEntityTypes;
import net.minecraft.world.level.block.state.IBlockData; import net.minecraft.world.level.block.state.IBlockData;
@@ -91,14 +88,6 @@ public class ProtocolWrapper18 implements ProtocolWrapper {
return tileEntityType.get(packet) != signType; return tileEntityType.get(packet) != signType;
} }
private static final IRegistry<?> tileEntityTypes = Reflection.getField(Core.getVersion() > 18 ? Reflection.getClass("net.minecraft.core.registries.BuiltInRegistries") : IRegistry.class, IRegistry.class, 0, TileEntityTypes.class).get(null);
private static final Reflection.Method getKey = Reflection.getTypedMethod(IRegistry.class, null, MinecraftKey.class, Object.class);
private static final Reflection.Method getName = Reflection.getTypedMethod(MinecraftKey.class, null, String.class);
@Override
public String tileEntityDataType(Object packet) {
return TechHider.normalizeBlockEntityId((String) getName.invoke(getKey.invoke(tileEntityTypes, tileEntityType.get(packet))));
}
@Override @Override
public BiFunction<Player, Object, Object> blockBreakHiderGenerator(Class<?> blockBreakPacket, TechHider techHider) { public BiFunction<Player, Object, Object> blockBreakHiderGenerator(Class<?> blockBreakPacket, TechHider techHider) {
return (p, packet) -> { return (p, packet) -> {
@@ -19,7 +19,6 @@
package de.steamwar.techhider; package de.steamwar.techhider;
import de.steamwar.Reflection;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import java.util.function.BiFunction; import java.util.function.BiFunction;
@@ -28,7 +27,6 @@ public class ProtocolWrapper19 extends ProtocolWrapper18 {
@Override @Override
public BiFunction<Player, Object, Object> blockBreakHiderGenerator(Class<?> blockBreakPacket, TechHider techHider) { public BiFunction<Player, Object, Object> blockBreakHiderGenerator(Class<?> blockBreakPacket, TechHider techHider) {
Reflection.Field<?> blockBreakPosition = Reflection.getField(blockBreakPacket, TechHider.blockPosition, 0); return null;
return (p, packet) -> techHider.getLocationEvaluator().checkBlockPos(p, blockBreakPosition.get(packet)) == TechHider.State.SKIP ? packet : null;
} }
} }
@@ -25,20 +25,16 @@ import io.netty.buffer.Unpooled;
import net.minecraft.network.protocol.game.ClientboundLevelChunkPacketData; import net.minecraft.network.protocol.game.ClientboundLevelChunkPacketData;
import net.minecraft.network.protocol.game.ClientboundLevelChunkWithLightPacket; import net.minecraft.network.protocol.game.ClientboundLevelChunkWithLightPacket;
import net.minecraft.util.SimpleBitStorage; import net.minecraft.util.SimpleBitStorage;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.entity.BlockEntityType; import net.minecraft.world.level.block.entity.BlockEntityType;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import java.util.function.IntUnaryOperator;
import java.util.function.BiFunction; import java.util.function.BiFunction;
import java.util.function.UnaryOperator; import java.util.function.UnaryOperator;
import java.util.stream.Collectors; import java.util.stream.Collectors;
public class ChunkHider21 implements ChunkHider { public class ChunkHider21 implements ChunkHider {
private static final int DIRECT_BLOCK_BITS = Math.max(9, Integer.SIZE - Integer.numberOfLeadingZeros(Block.BLOCK_STATE_REGISTRY.size() - 1));
@Override @Override
public Class<?> mapChunkPacket() { public Class<?> mapChunkPacket() {
return ClientboundLevelChunkWithLightPacket.class; return ClientboundLevelChunkWithLightPacket.class;
@@ -100,70 +96,33 @@ public class ChunkHider21 implements ChunkHider {
private static final Reflection.Method getKey = Reflection.getTypedMethod(registry, "getKey", resourceLocation, Object.class); private static final Reflection.Method getKey = Reflection.getTypedMethod(registry, "getKey", resourceLocation, Object.class);
private static final Reflection.Method getName = Reflection.getTypedMethod(resourceLocation, "getPath", String.class); private static final Reflection.Method getName = Reflection.getTypedMethod(resourceLocation, "getPath", String.class);
protected boolean tileEntityVisible(Set<String> hiddenBlockEntities, Object tile) { protected boolean tileEntityVisible(Set<String> hiddenBlockEntities, Object tile) {
return !hiddenBlockEntities.contains(TechHider.normalizeBlockEntityId((String) getName.invoke(getKey.invoke(nameField.get(null), entityType.get(tile))))); return !hiddenBlockEntities.contains(getName.invoke(getKey.invoke(nameField.get(null), entityType.get(tile))));
} }
private void blocks(SectionHider section) { private void blocks(SectionHider section) {
int sourceBitsPerBlock = section.readBitsPerBlock(); section.copyBitsPerBlock();
if(section.isSkipSection()) { boolean singleValued = section.getBitsPerBlock() == 0;
section.writeBitsPerBlock(sourceBitsPerBlock);
copyBlockStorage(section);
return;
}
boolean singleValued = sourceBitsPerBlock == 0;
if (singleValued) { if (singleValued) {
int value = ProtocolUtils.readVarInt(section.getIn()); int value = ProtocolUtils.readVarInt(section.getIn());
if(section.blockPrecise()) { ProtocolUtils.writeVarInt(section.getOut(), !section.isSkipSection() && section.getObfuscate().contains(value) ? section.getTarget() : value);
writeDirectBlocks(section, DIRECT_BLOCK_BITS, ignored -> value);
} else {
section.writeBitsPerBlock(sourceBitsPerBlock);
ProtocolUtils.writeVarInt(section.getOut(), section.getObfuscate().contains(value) ? section.getTarget() : value);
}
return; return;
} else if (sourceBitsPerBlock < 9) { } else if (section.getBitsPerBlock() < 9) {
if(section.blockPrecise()) {
int[] palette = section.readPalette();
SimpleBitStorage values = new SimpleBitStorage(sourceBitsPerBlock, 4096, section.readNewDataArray(4096));
writeDirectBlocks(section, DIRECT_BLOCK_BITS, pos -> palette[values.get(pos)]);
return;
}
section.writeBitsPerBlock(sourceBitsPerBlock);
// Indirect (paletted) storage – only present when bitsPerBlock < 9 in 1.21+ // Indirect (paletted) storage – only present when bitsPerBlock < 9 in 1.21+
section.processPalette(); section.processPalette();
} }
if (!section.blockPrecise() && section.isPaletted()) { if (section.isSkipSection() || (!section.blockPrecise() && section.isPaletted())) {
section.skipNewDataArray(4096); section.skipNewDataArray(4096);
return; return;
} }
SimpleBitStorage values = new SimpleBitStorage(sourceBitsPerBlock, 4096, section.readNewDataArray(4096)); SimpleBitStorage values = new SimpleBitStorage(section.getBitsPerBlock(), 4096, section.readNewDataArray(4096));
writeDirectBlocks(section, sourceBitsPerBlock, values::get);
}
private void copyBlockStorage(SectionHider section) {
if(section.getBitsPerBlock() == 0) {
section.copyVarInt();
} else {
if(section.getBitsPerBlock() < 9)
section.skipPalette();
section.skipNewDataArray(4096);
}
}
private void writeDirectBlocks(SectionHider section, int bitsPerBlock, IntUnaryOperator source) {
section.writeBitsPerBlock(bitsPerBlock);
SimpleBitStorage values = new SimpleBitStorage(bitsPerBlock, 4096);
for (int y = 0; y < 16; y++) { for (int y = 0; y < 16; y++) {
for (int z = 0; z < 16; z++) { for (int z = 0; z < 16; z++) {
for (int x = 0; x < 16; x++) { for (int x = 0; x < 16; x++) {
int pos = (((y * 16) + z) * 16) + x; int pos = (((y * 16) + z) * 16) + x;
int value = source.applyAsInt(pos);
TechHider.State test = section.test(x, y, z); TechHider.State test = section.test(x, y, z);
@@ -171,17 +130,15 @@ public class ChunkHider21 implements ChunkHider {
case SKIP: case SKIP:
break; break;
case CHECK: case CHECK:
if (!section.getObfuscate().contains(value)) if (!section.getObfuscate().contains(values.get(pos)))
break; break;
case HIDE: case HIDE:
value = section.getTarget(); values.set(pos, section.getTarget());
break; break;
case HIDE_AIR: case HIDE_AIR:
default: default:
value = section.getAir(); values.set(pos, section.getAir());
} }
values.set(pos, value);
} }
} }
} }
@@ -1,104 +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.techhider;
import de.steamwar.Reflection;
import net.minecraft.core.BlockPos;
import net.minecraft.core.SectionPos;
import net.minecraft.network.protocol.game.ClientboundBlockDestructionPacket;
import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraft.world.level.block.state.BlockState;
import org.bukkit.entity.Player;
import java.util.ArrayList;
import java.util.function.BiFunction;
public class ProtocolWrapper21 implements ProtocolWrapper {
private static final Reflection.Field<SectionPos> multiBlockChangeChunk = Reflection.getField(TechHider.multiBlockChangePacket, SectionPos.class, 0);
private static final Reflection.Field<short[]> multiBlockChangePos = Reflection.getField(TechHider.multiBlockChangePacket, short[].class, 0);
private static final Reflection.Field<BlockState[]> multiBlockChangeBlocks = Reflection.getField(TechHider.multiBlockChangePacket, BlockState[].class, 0);
@Override
public BiFunction<Player, Object, Object> multiBlockChangeGenerator(TechHider techHider) {
return (p, packet) -> {
TechHider.LocationEvaluator locationEvaluator = techHider.getLocationEvaluator();
SectionPos sectionPos = multiBlockChangeChunk.get(packet);
int chunkX = sectionPos.x();
int chunkY = sectionPos.y();
int chunkZ = sectionPos.z();
if(locationEvaluator.skipChunkSection(p, chunkX, chunkY, chunkZ))
return packet;
packet = TechHider.multiBlockChangeCloner.apply(packet);
final short[] oldPos = multiBlockChangePos.get(packet);
final BlockState[] oldBlocks = multiBlockChangeBlocks.get(packet);
ArrayList<Short> poss = new ArrayList<>(oldPos.length);
ArrayList<BlockState> blocks = new ArrayList<>(oldPos.length);
for(int i = 0; i < oldPos.length; i++) {
short pos = oldPos[i];
BlockState block = oldBlocks[i];
switch(locationEvaluator.check(p, 16*chunkX + SectionPos.sectionRelativeX(pos), 16*chunkY + SectionPos.sectionRelativeY(pos), 16*chunkZ + SectionPos.sectionRelativeZ(pos))) {
case SKIP:
poss.add(pos);
blocks.add(block);
break;
case CHECK:
poss.add(pos);
blocks.add(techHider.iBlockDataHidden(block) ? (BlockState) techHider.getObfuscationTarget() : block);
break;
default:
break;
}
}
if(blocks.isEmpty())
return null;
short[] newPos = new short[poss.size()];
for(int i = 0; i < newPos.length; i++)
newPos[i] = poss.get(i);
multiBlockChangePos.set(packet, newPos);
multiBlockChangeBlocks.set(packet, blocks.toArray(new BlockState[0]));
return packet;
};
}
private static final Reflection.Field<BlockEntityType> tileEntityType = Reflection.getField(TechHider.tileEntityDataPacket, BlockEntityType.class, 0);
@Override
public boolean unfilteredTileEntityDataAction(Object packet) {
BlockEntityType type = tileEntityType.get(packet);
return type != BlockEntityType.SIGN && type != BlockEntityType.HANGING_SIGN;
}
@Override
public String tileEntityDataType(Object packet) {
return BlockEntityType.getKey(tileEntityType.get(packet)).getPath();
}
private static final Reflection.Field<BlockPos> blockBreakPosition = Reflection.getField(ClientboundBlockDestructionPacket.class, BlockPos.class, 0);
@Override
public BiFunction<Player, Object, Object> blockBreakHiderGenerator(Class<?> blockBreakPacket, TechHider techHider) {
return (p, packet) -> techHider.getLocationEvaluator().checkBlockPos(p, blockBreakPosition.get(packet)) == TechHider.State.SKIP ? packet : null;
}
}
@@ -54,7 +54,7 @@ public class ChunkHider9 extends ChunkHider8 {
packet = mapChunkCloner.apply(packet); packet = mapChunkCloner.apply(packet);
Set<String> hiddenBlockEntities = techHider.getHiddenBlockEntities(); Set<String> hiddenBlockEntities = techHider.getHiddenBlockEntities();
mapChunkBlockEntities.set(packet, ((List<?>)mapChunkBlockEntities.get(packet)).stream().filter( mapChunkBlockEntities.set(packet, ((List<?>)mapChunkBlockEntities.get(packet)).stream().filter(
nbttag -> !hiddenBlockEntities.contains(TechHider.normalizeBlockEntityId((String) nbtTagGetString.invoke(nbttag, "id"))) nbttag -> !hiddenBlockEntities.contains((String) nbtTagGetString.invoke(nbttag, "id"))
).collect(Collectors.toList())); ).collect(Collectors.toList()));
int primaryBitMask = mapChunkBitMask.get(packet); int primaryBitMask = mapChunkBitMask.get(packet);
@@ -23,7 +23,6 @@ import de.steamwar.Reflection;
import de.steamwar.Reflection.Field; import de.steamwar.Reflection.Field;
import de.steamwar.core.Core; import de.steamwar.core.Core;
import io.netty.channel.*; import io.netty.channel.*;
import io.netty.util.ReferenceCountUtil;
import lombok.Getter; import lombok.Getter;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
@@ -38,7 +37,6 @@ import org.bukkit.plugin.Plugin;
import java.util.*; import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.BiFunction; import java.util.function.BiFunction;
import java.util.logging.Level; import java.util.logging.Level;
@@ -59,29 +57,17 @@ public class TinyProtocol implements Listener {
public static final Field<List> networkManagers = Reflection.getField(serverConnection, List.class, 0, networkManager); public static final Field<List> networkManagers = Reflection.getField(serverConnection, List.class, 0, networkManager);
private static final String HANDLER_NAME = "tiny-steamwar"; private static final String HANDLER_NAME = "tiny-steamwar";
private static final Class<?> bundlePacket = getOptionalClass("net.minecraft.network.protocol.BundlePacket");
private static final Class<?> clientboundBundlePacket = getOptionalClass("net.minecraft.network.protocol.game.ClientboundBundlePacket");
private static final Reflection.Method bundleSubPackets = bundlePacket == null ? null : Reflection.getTypedMethod(bundlePacket, null, Iterable.class);
private static final Reflection.Constructor clientboundBundleConstructor = clientboundBundlePacket == null ? null : Reflection.getConstructor(clientboundBundlePacket, Iterable.class);
public static final TinyProtocol instance = new TinyProtocol(Core.getInstance()); public static final TinyProtocol instance = new TinyProtocol(Core.getInstance());
public static void init() { public static void init() {
//enforce init //enforce init
} }
private static Class<?> getOptionalClass(String className) {
try {
return Reflection.getClass(className);
} catch (IllegalArgumentException ignored) {
return null;
}
}
private final Plugin plugin; private final Plugin plugin;
private final List<?> connections; private final List<?> connections;
private boolean closed; private boolean closed;
private final Map<Class<?>, List<BiFunction<Player, Object, Object>>> packetFilters = new ConcurrentHashMap<>(); private final Map<Class<?>, List<BiFunction<Player, Object, Object>>> packetFilters = new HashMap<>();
@Getter @Getter
private final Map<Player, PacketInterceptor> playerInterceptors = new HashMap<>(); private final Map<Player, PacketInterceptor> playerInterceptors = new HashMap<>();
@@ -221,51 +207,31 @@ public class TinyProtocol implements Listener {
@Override @Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
Object originalMsg = msg;
try { try {
msg = filterPacket(player, msg); msg = filterPacket(player, msg);
} catch (Exception e) { } catch (Exception e) {
plugin.getLogger().log(Level.SEVERE, "Error during incoming packet processing", e); plugin.getLogger().log(Level.SEVERE, "Error during incoming packet processing", e);
msg = null;
} }
if (msg != null) { if (msg != null) {
super.channelRead(ctx, msg); super.channelRead(ctx, msg);
} else {
ReferenceCountUtil.release(originalMsg);
} }
} }
@Override @Override
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception { public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
Object originalMsg = msg;
try { try {
msg = filterPacket(player, msg); msg = filterPacket(player, msg);
} catch (Exception e) { } catch (Exception e) {
plugin.getLogger().log(Level.SEVERE, "Error during outgoing packet processing", e); plugin.getLogger().log(Level.SEVERE, "Error during outgoing packet processing", e);
msg = null;
} }
if (msg != null) { if (msg != null) {
super.write(ctx, msg, promise); super.write(ctx, msg, promise);
} else {
ReferenceCountUtil.release(originalMsg);
promise.trySuccess();
} }
} }
private Object filterPacket(Player player, Object packet) { private Object filterPacket(Player player, Object packet) {
packet = filterSinglePacket(player, packet);
if(packet == null)
return null;
if(clientboundBundlePacket != null && clientboundBundlePacket.isInstance(packet))
return filterBundlePacket(player, packet);
return packet;
}
private Object filterSinglePacket(Player player, Object packet) {
List<BiFunction<Player, Object, Object>> filters = packetFilters.getOrDefault(packet.getClass(), Collections.emptyList()); List<BiFunction<Player, Object, Object>> filters = packetFilters.getOrDefault(packet.getClass(), Collections.emptyList());
for(BiFunction<Player, Object, Object> filter : filters) { for(BiFunction<Player, Object, Object> filter : filters) {
@@ -277,33 +243,5 @@ public class TinyProtocol implements Listener {
return packet; return packet;
} }
private Object filterBundlePacket(Player player, Object packet) {
if(bundleSubPackets == null || clientboundBundleConstructor == null)
throw new IllegalStateException("Cannot filter bundled packet " + packet.getClass().getName());
ArrayList<Object> filteredPackets = new ArrayList<>();
boolean changed = false;
for(Object subPacket : (Iterable<?>) bundleSubPackets.invoke(packet)) {
Object filteredPacket = filterPacket(player, subPacket);
if(filteredPacket == null) {
changed = true;
continue;
}
if(filteredPacket != subPacket)
changed = true;
filteredPackets.add(filteredPacket);
}
if(!changed)
return packet;
if(filteredPackets.isEmpty())
return null;
return clientboundBundleConstructor.invoke(filteredPackets);
}
} }
} }
@@ -92,17 +92,7 @@ public interface ChunkHider {
} }
public void copyBitsPerBlock() { public void copyBitsPerBlock() {
bitsPerBlock = in.readUnsignedByte(); bitsPerBlock = in.readByte();
out.writeByte(bitsPerBlock);
}
public int readBitsPerBlock() {
bitsPerBlock = in.readUnsignedByte();
return bitsPerBlock;
}
public void writeBitsPerBlock(int bitsPerBlock) {
this.bitsPerBlock = bitsPerBlock;
out.writeByte(bitsPerBlock); out.writeByte(bitsPerBlock);
} }
@@ -118,15 +108,6 @@ public interface ChunkHider {
copyVarInt(); copyVarInt();
} }
public int[] readPalette() {
int paletteLength = ProtocolUtils.readVarInt(in);
int[] palette = new int[paletteLength];
for(int i = 0; i < paletteLength; i++)
palette[i] = ProtocolUtils.readVarInt(in);
return palette;
}
public void processPalette() { public void processPalette() {
if(skipSection) { if(skipSection) {
skipPalette(); skipPalette();
@@ -98,7 +98,10 @@ public class ProtocolUtils {
} }
public static int posToChunk(int c){ public static int posToChunk(int c){
return Math.floorDiv(c, 16); int chunk = c / 16;
if(c < 0)
chunk--;
return chunk;
} }
@Deprecated @Deprecated
@@ -31,10 +31,6 @@ public interface ProtocolWrapper {
boolean unfilteredTileEntityDataAction(Object packet); boolean unfilteredTileEntityDataAction(Object packet);
default String tileEntityDataType(Object packet) {
return null;
}
BiFunction<Player, Object, Object> blockBreakHiderGenerator(Class<?> blockBreakPacket, TechHider techHider); BiFunction<Player, Object, Object> blockBreakHiderGenerator(Class<?> blockBreakPacket, TechHider techHider);
BiFunction<Player, Object, Object> multiBlockChangeGenerator(TechHider techHider); BiFunction<Player, Object, Object> multiBlockChangeGenerator(TechHider techHider);
@@ -27,7 +27,6 @@ import org.bukkit.Material;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import java.util.HashMap; import java.util.HashMap;
import java.util.Locale;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.function.BiFunction; import java.util.function.BiFunction;
@@ -68,12 +67,11 @@ public class TechHider {
private final Set<Integer> obfuscateIds; private final Set<Integer> obfuscateIds;
@Getter @Getter
private final Set<String> hiddenBlockEntities; private final Set<String> hiddenBlockEntities;
private boolean enabled;
public TechHider(LocationEvaluator locationEvaluator, Material obfuscationTarget, Set<Material> obfuscate, Set<String> hiddenBlockEntities) { public TechHider(LocationEvaluator locationEvaluator, Material obfuscationTarget, Set<Material> obfuscate, Set<String> hiddenBlockEntities) {
this.locationEvaluator = locationEvaluator; this.locationEvaluator = locationEvaluator;
this.obfuscateIds = obfuscate.stream().flatMap(m -> BlockIds.impl.materialToAllIds(m).stream()).collect(Collectors.toSet()); this.obfuscateIds = obfuscate.stream().flatMap(m -> BlockIds.impl.materialToAllIds(m).stream()).collect(Collectors.toSet());
this.hiddenBlockEntities = hiddenBlockEntities.stream().map(TechHider::normalizeBlockEntityId).collect(Collectors.toSet()); this.hiddenBlockEntities = hiddenBlockEntities;
this.obfuscationTarget = getBlockDataByBlock.invoke(getBlockByMaterial.invoke(null, obfuscationTarget)); this.obfuscationTarget = getBlockDataByBlock.invoke(getBlockByMaterial.invoke(null, obfuscationTarget));
this.obfuscationTargetId = BlockIds.impl.materialToId(obfuscationTarget); this.obfuscationTargetId = BlockIds.impl.materialToId(obfuscationTarget);
@@ -83,11 +81,9 @@ public class TechHider {
techhiders.put(multiBlockChangePacket, ProtocolWrapper.impl.multiBlockChangeGenerator(this)); techhiders.put(multiBlockChangePacket, ProtocolWrapper.impl.multiBlockChangeGenerator(this));
techhiders.put(ChunkHider.impl.mapChunkPacket(), ChunkHider.impl.chunkHiderGenerator(this)); techhiders.put(ChunkHider.impl.mapChunkPacket(), ChunkHider.impl.chunkHiderGenerator(this));
if(Core.getVersion() > 12 && Core.getVersion() < 19 || Core.getVersion() >= 21) { if(Core.getVersion() > 12 && Core.getVersion() < 19) {
Class<?> blockBreakClass = Reflection.getClass("net.minecraft.network.protocol.game.ClientboundBlockDestructionPacket"); Class<?> blockBreakClass = Reflection.getClass("net.minecraft.network.protocol.game.ClientboundBlockDestructionPacket");
BiFunction<Player, Object, Object> blockBreakHider = ProtocolWrapper.impl.blockBreakHiderGenerator(blockBreakClass, this); techhiders.put(blockBreakClass, ProtocolWrapper.impl.blockBreakHiderGenerator(blockBreakClass, this));
if(blockBreakHider != null)
techhiders.put(blockBreakClass, blockBreakHider);
} }
if(Core.getVersion() > 8){ if(Core.getVersion() > 8){
@@ -98,18 +94,10 @@ public class TechHider {
} }
public void enable() { public void enable() {
if(enabled)
return;
enabled = true;
techhiders.forEach(TinyProtocol.instance::addFilter); techhiders.forEach(TinyProtocol.instance::addFilter);
} }
public void disable() { public void disable() {
if(!enabled)
return;
enabled = false;
techhiders.forEach(TinyProtocol.instance::removeFilter); techhiders.forEach(TinyProtocol.instance::removeFilter);
} }
@@ -154,8 +142,6 @@ public class TechHider {
case SKIP: case SKIP:
return packet; return packet;
case CHECK: case CHECK:
if(isHiddenBlockEntity(ProtocolWrapper.impl.tileEntityDataType(packet)))
return null;
if(ProtocolWrapper.impl.unfilteredTileEntityDataAction(packet)) if(ProtocolWrapper.impl.unfilteredTileEntityDataAction(packet))
return packet; return packet;
default: default:
@@ -163,19 +149,6 @@ public class TechHider {
} }
} }
public boolean isHiddenBlockEntity(String id) {
return hiddenBlockEntities.contains(normalizeBlockEntityId(id));
}
public static String normalizeBlockEntityId(String id) {
if(id == null)
return "";
id = id.toLowerCase(Locale.ROOT);
int namespaceSeparator = id.indexOf(':');
return namespaceSeparator < 0 ? id : id.substring(namespaceSeparator + 1);
}
public enum State { public enum State {
SKIP, SKIP,
CHECK, CHECK,
@@ -22,6 +22,7 @@ package de.steamwar.sql;
import com.velocitypowered.api.proxy.Player; import com.velocitypowered.api.proxy.Player;
import com.velocitypowered.api.proxy.server.RegisteredServer; import com.velocitypowered.api.proxy.server.RegisteredServer;
import de.steamwar.velocitycore.VelocityCore; import de.steamwar.velocitycore.VelocityCore;
import de.steamwar.velocitycore.commands.CheckCommand;
import java.io.File; import java.io.File;
@@ -37,6 +38,15 @@ public class SQLWrapperImpl implements SQLWrapper<String> {
return new GameModeConfig<>(file, GameModeConfig.ToString, GameModeConfig.ToString, GameModeConfig.ToInternalName, true); return new GameModeConfig<>(file, GameModeConfig.ToString, GameModeConfig.ToString, GameModeConfig.ToInternalName, true);
} }
@Override
public void processSchematicType(GameModeConfig<?, String> gameModeConfig) {
SchematicType type = gameModeConfig.Schematic.Type;
if (type.checkType() != null) {
CheckCommand.setCheckQuestions(type.checkType(), gameModeConfig.CheckQuestions);
CheckCommand.addFightType(type.checkType(), type);
}
}
@Override @Override
public void additionalExceptionMetadata(StringBuilder builder) { public void additionalExceptionMetadata(StringBuilder builder) {
builder.append("\nServers: "); builder.append("\nServers: ");
@@ -25,10 +25,7 @@ import lombok.Getter;
import lombok.experimental.UtilityClass; import lombok.experimental.UtilityClass;
import java.io.File; import java.io.File;
import java.util.HashMap; import java.util.*;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
@UtilityClass @UtilityClass
public class ArenaMode { public class ArenaMode {
@@ -53,12 +50,12 @@ public class ArenaMode {
if(!folder.exists()) if(!folder.exists())
return; return;
GameModeConfig.init(); for(File file : Arrays.stream(folder.listFiles((file, name) -> name.endsWith(".yml") && !name.endsWith(".kits.yml") && !name.equals("config.yml"))).sorted().toList()) {
SchematicType.init(); GameModeConfig<String, String> gameModeConfig = new GameModeConfig<>(file, GameModeConfig.ToString, GameModeConfig.ToString, GameModeConfig.ToInternalName, false);
for (GameModeConfig<String, String> gameModeConfig : GameModeConfig.<String>getAll()) {
if (!gameModeConfig.Server.loaded) continue; if (!gameModeConfig.Server.loaded) continue;
allModes.add(gameModeConfig); allModes.add(gameModeConfig);
byInternal.put(gameModeConfig.configFile.getName().replace(".yml", ""), gameModeConfig); byInternal.put(file.getName().replace(".yml", ""), gameModeConfig);
for (String name : gameModeConfig.Server.ChatNames) { for (String name : gameModeConfig.Server.ChatNames) {
byChat.put(name.toLowerCase(), gameModeConfig); byChat.put(name.toLowerCase(), gameModeConfig);
} }
@@ -40,7 +40,6 @@ import net.kyori.adventure.text.format.NamedTextColor;
import java.awt.*; import java.awt.*;
import java.sql.Timestamp; import java.sql.Timestamp;
import java.time.Instant; import java.time.Instant;
import java.time.format.DateTimeFormatter;
import java.util.*; import java.util.*;
import java.util.List; import java.util.List;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
@@ -49,9 +48,20 @@ import java.util.logging.Level;
@Linked @Linked
public class CheckCommand extends SWCommand { public class CheckCommand extends SWCommand {
private static final Map<SchematicType, SchematicType> fightTypes = new HashMap<>();
private static final Map<SchematicType, List<String>> checkQuestions = new HashMap<>();
private static final Map<UUID, CheckSession> currentCheckers = new HashMap<>(); private static final Map<UUID, CheckSession> currentCheckers = new HashMap<>();
private static final Map<Integer, CheckSession> currentSchems = new HashMap<>(); private static final Map<Integer, CheckSession> currentSchems = new HashMap<>();
public static void setCheckQuestions(SchematicType checkType, List<String> checkQuestions) {
CheckCommand.checkQuestions.put(checkType, checkQuestions);
}
public static void addFightType(SchematicType checkType, SchematicType fightType) {
fightTypes.put(checkType, fightType);
}
public static boolean isChecking(Player player){ public static boolean isChecking(Player player){
return currentCheckers.containsKey(player.getUniqueId()); return currentCheckers.containsKey(player.getUniqueId());
} }
@@ -137,7 +147,7 @@ public class CheckCommand extends SWCommand {
if(!schem.getSchemtype().check()){ if(!schem.getSchemtype().check()){
VelocityCore.getLogger().log(Level.SEVERE, () -> sender.user().getUserName() + " tried to check an uncheckable schematic!"); VelocityCore.getLogger().log(Level.SEVERE, () -> sender.user().getUserName() + " tried to check an uncheckable schematic!");
return; return;
}else if(schem.getOwner() == sender.user().getId() && !sender.user().hasPerm(UserPerm.ADMINISTRATION)) { }else if(schem.getOwner() == sender.user().getId()) {
sender.system("CHECK_SCHEMATIC_OWN"); sender.system("CHECK_SCHEMATIC_OWN");
return; return;
} }
@@ -238,9 +248,9 @@ public class CheckCommand extends SWCommand {
this.checker = checker; this.checker = checker;
this.schematic = schematic; this.schematic = schematic;
this.startTime = Timestamp.from(Instant.now()); this.startTime = Timestamp.from(Instant.now());
this.checkList = GameModeConfig.getBySchematicType(schematic.getSchemtype()).CheckQuestions.listIterator(); this.checkList = checkQuestions.get(schematic.getSchemtype()).listIterator();
GameModeConfig<String, String> mode = GameModeConfig.getBySchematicType(schematic.getSchemtype()); GameModeConfig<String, String> mode = ArenaMode.getBySchemType(fightTypes.get(schematic.getSchemtype()));
new ServerStarter().test(mode, mode.getRandomMap(), checker.getPlayer()).check(schematic.getId()).callback(subserver -> { new ServerStarter().test(mode, mode.getRandomMap(), checker.getPlayer()).check(schematic.getId()).callback(subserver -> {
currentCheckers.put(checker.user().getUUID(), this); currentCheckers.put(checker.user().getUUID(), this);
currentSchems.put(schematic.getId(), this); currentSchems.put(schematic.getId(), this);
@@ -294,50 +304,7 @@ public class CheckCommand extends SWCommand {
} }
private void accept(){ private void accept(){
// TODO: This Code is only for the WGS and because YoyoNow is not available this can be removed or changed after the WGS! concludeCheckSession("freigegeben", fightTypes.get(schematic.getSchemtype()), () -> {
if (schematic.getSchemtype().toDB().equals("cwargearseason26")) {
int userId = schematic.getOwner();
SteamwarUser user = SteamwarUser.byId(userId);
int teamId = user.getTeam();
SchematicNode teamFolder = SchematicNode.getSchematicNodeInNode(172325)
.stream()
.filter(schematicNode -> schematicNode.getName().startsWith(teamId + "_"))
.findFirst()
.orElse(null);
if (teamFolder == null) {
internalAccept();
return;
}
// Copy Schem into team folder of -1 user
String name = DateTimeFormatter.ofPattern("yyyy.MM.dd_HH:mm:ss").format(schematic.getLastUpdate().toLocalDateTime());
NodeData data = NodeData.getLatest(schematic);
SchematicNode node = SchematicNode.createSchematic(-1, name, teamFolder.getNodeId());
NodeData.saveFromStream(node, data.schemData(false), data.getNodeFormat());
// Accept the team folder schematic and set other to Normal
node.setSchemtype(GameModeConfig.getBySchematicType(schematic.getSchemtype()).Schematic.Type);
// Conclude by setting send in schematic to normal and broadcast
concludeCheckSession("freigegeben", SchematicType.Normal, () -> {
Chatter owner = Chatter.of(SteamwarUser.byId(schematic.getOwner()).getUUID());
owner.withPlayerOrOffline(
player -> owner.system("CHECK_ACCEPTED", schematic.getSchemtype().name(), schematic.getName()),
() -> DiscordAlert.send(owner, Color.GREEN, new Message("DC_TITLE_SCHEMINFO"), new Message("DC_SCHEM_ACCEPT", schematic.getName()), true)
);
notifyTeam(new Message("CHECK_ACCEPTED_TEAM", schematic.getName(), owner.user().getUserName()));
return owner.getPlayer() != null;
});
return;
}
internalAccept();
}
private void internalAccept() {
concludeCheckSession("freigegeben", GameModeConfig.getBySchematicType(schematic.getSchemtype()).Schematic.Type, () -> {
Chatter owner = Chatter.of(SteamwarUser.byId(schematic.getOwner()).getUUID()); Chatter owner = Chatter.of(SteamwarUser.byId(schematic.getOwner()).getUUID());
owner.withPlayerOrOffline( owner.withPlayerOrOffline(
player -> owner.system("CHECK_ACCEPTED", schematic.getSchemtype().name(), schematic.getName()), player -> owner.system("CHECK_ACCEPTED", schematic.getSchemtype().name(), schematic.getName()),
@@ -50,7 +50,7 @@ public class WhoisCommand extends SWCommand {
@Register(description = "WHOIS_USAGE") @Register(description = "WHOIS_USAGE")
public void whois(Chatter sender, long id, WhoisParameterTypes... parameters) { public void whois(Chatter sender, long id, WhoisParameterTypes... parameters) {
if(!sender.user().hasPerm(UserPerm.ADMINISTRATION) && !sender.user().hasPerm(UserPerm.PREFIX_DEVELOPER)) { if(!sender.user().hasPerm(UserPerm.ADMINISTRATION)) {
sender.system("UNKNOWN_PLAYER"); sender.system("UNKNOWN_PLAYER");
return; return;
} }
@@ -158,6 +158,7 @@ public class Tablist extends ChannelInboundHandlerAdapter {
public void disable() { public void disable() {
sendTabPacket(new ArrayList<>(directTabItems.values()), null); sendTabPacket(new ArrayList<>(directTabItems.values()), null);
directTabItems.clear();
sendTabPacket(current, null); sendTabPacket(current, null);
current.clear(); current.clear();
@@ -55,7 +55,7 @@ public class BauLock {
break; break;
case SUPERVISOR: case SUPERVISOR:
BauweltMember member = BauweltMember.getBauMember(owner.getId(), target.getId()); BauweltMember member = BauweltMember.getBauMember(owner.getId(), target.getId());
locked = member == null || !member.isSupervisor(); locked = !member.isSupervisor();
break; break;
case SERVERTEAM: case SERVERTEAM:
locked = !target.hasPerm(UserPerm.TEAM); locked = !target.hasPerm(UserPerm.TEAM);
-2
View File
@@ -101,7 +101,6 @@ dependencyResolutionManagement {
library("hamcrest", "org.hamcrest:hamcrest:2.2") library("hamcrest", "org.hamcrest:hamcrest:2.2")
library("classindex", "org.atteo.classindex:classindex:3.13") library("classindex", "org.atteo.classindex:classindex:3.13")
library("spigotapi", "org.spigotmc:spigot-api:1.20-R0.1-SNAPSHOT") library("spigotapi", "org.spigotmc:spigot-api:1.20-R0.1-SNAPSHOT")
library("spigotannotations", "org.spigotmc:plugin-annotations:1.2.3-SNAPSHOT") library("spigotannotations", "org.spigotmc:plugin-annotations:1.2.3-SNAPSHOT")
library("paperapi", "io.papermc.paper:paper-api:1.19.2-R0.1-SNAPSHOT") library("paperapi", "io.papermc.paper:paper-api:1.19.2-R0.1-SNAPSHOT")
@@ -223,7 +222,6 @@ include(
"SchematicSystem:SchematicSystem_15", "SchematicSystem:SchematicSystem_15",
"SchematicSystem:SchematicSystem_19", "SchematicSystem:SchematicSystem_19",
"SchematicSystem:SchematicSystem_20", "SchematicSystem:SchematicSystem_20",
"SchematicSystem:SchematicSystem_21",
"SchematicSystem:SchematicSystem_Core" "SchematicSystem:SchematicSystem_Core"
) )